Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
authorDavid S. Miller <davem@davemloft.net>
Mon, 18 Feb 2019 19:38:30 +0000 (11:38 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 18 Feb 2019 19:38:30 +0000 (11:38 -0800)
Pablo Neira Ayuso says:

====================
Netfilter/IPVS updates for net-next

The following patchset contains Netfilter/IPVS updates for you net-next
tree:

1) Missing NFTA_RULE_POSITION_ID netlink attribute validation,
   from Phil Sutter.

2) Restrict matching on tunnel metadata to rx/tx path, from wenxu.

3) Avoid indirect calls for IPV6=y, from Florian Westphal.

4) Add two indirections to prepare merger of IPV4 and IPV6 nat
   modules, from Florian Westphal.

5) Broken indentation in ctnetlink, from Colin Ian King.

6) Patches to use struct_size() from netfilter and IPVS,
   from Gustavo A. R. Silva.

7) Display kernel splat only once in case of racing to confirm
   conntrack from bridge plus nfqueue setups, from Chieh-Min Wang.

8) Skip checksum validation for layer 4 protocols that don't need it,
   patch from Alin Nastac.

9) Sparse warning due to symbol that should be static in CLUSTERIP,
   from Wei Yongjun.

10) Add new toggle to disable SDP payload translation when media
    endpoint is reachable though the same interface as the signalling
    peer, from Alin Nastac.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
1598 files changed:
Documentation/ABI/stable/sysfs-driver-mlxreg-io
Documentation/admin-guide/kernel-parameters.txt
Documentation/devicetree/bindings/Makefile
Documentation/devicetree/bindings/net/dsa/mt7530.txt
Documentation/devicetree/bindings/net/macb.txt
Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
Documentation/devicetree/bindings/net/nixge.txt
Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/ptp/ptp-qoriq.txt
Documentation/devicetree/bindings/serio/olpc,ap-sp.txt
Documentation/driver-api/80211/mac80211.rst
Documentation/networking/device_drivers/freescale/dpaa2/dpio-driver.rst
Documentation/networking/device_drivers/intel/e100.rst
Documentation/networking/device_drivers/intel/e1000.rst
Documentation/networking/device_drivers/intel/e1000e.rst
Documentation/networking/device_drivers/intel/fm10k.rst
Documentation/networking/device_drivers/intel/i40e.rst
Documentation/networking/device_drivers/intel/iavf.rst
Documentation/networking/device_drivers/intel/ice.rst
Documentation/networking/device_drivers/intel/igb.rst
Documentation/networking/device_drivers/intel/igbvf.rst
Documentation/networking/device_drivers/intel/ixgb.rst
Documentation/networking/device_drivers/intel/ixgbe.rst
Documentation/networking/device_drivers/intel/ixgbevf.rst
Documentation/networking/device_drivers/stmicro/stmmac.txt
Documentation/networking/devlink-health.txt [new file with mode: 0644]
Documentation/networking/devlink-info-versions.rst [new file with mode: 0644]
Documentation/networking/devlink-params-mlxsw.txt
Documentation/networking/filter.txt
Documentation/networking/index.rst
Documentation/networking/operstates.txt
Documentation/networking/snmp_counter.rst
Documentation/networking/timestamping.txt
Documentation/sysctl/fs.txt
Documentation/sysctl/net.txt
Documentation/x86/resctrl_ui.txt
MAINTAINERS
Makefile
arch/alpha/include/asm/irq.h
arch/alpha/include/uapi/asm/socket.h
arch/alpha/mm/fault.c
arch/arm/boot/dts/am335x-shc.dts
arch/arm/boot/dts/armada-388-clearfog.dtsi
arch/arm/boot/dts/armada-38x.dtsi
arch/arm/boot/dts/da850.dtsi
arch/arm/boot/dts/imx6q-pistachio.dts
arch/arm/boot/dts/imx6sll-evk.dts
arch/arm/boot/dts/imx6sx.dtsi
arch/arm/boot/dts/meson.dtsi
arch/arm/boot/dts/meson8b-ec100.dts
arch/arm/boot/dts/meson8b-odroidc1.dts
arch/arm/boot/dts/meson8m2-mxiii-plus.dts
arch/arm/boot/dts/motorola-cpcap-mapphone.dtsi
arch/arm/boot/dts/omap3-gta04.dtsi
arch/arm/boot/dts/omap3-n900.dts
arch/arm/boot/dts/omap3-n950-n9.dtsi
arch/arm/boot/dts/omap5-l4.dtsi
arch/arm/boot/dts/r8a7743.dtsi
arch/arm/boot/dts/sun6i-a31.dtsi
arch/arm/boot/dts/sun8i-h3-beelink-x2.dts
arch/arm/boot/dts/vf610-bk4.dts
arch/arm/mach-cns3xxx/pcie.c
arch/arm/mach-iop32x/n2100.c
arch/arm/mach-tango/pm.c
arch/arm/mach-tango/pm.h [new file with mode: 0644]
arch/arm/mach-tango/setup.c
arch/arm/plat-pxa/ssp.c
arch/arm/xen/mm.c
arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dts
arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95.dtsi
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek.dtsi
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-hwacom-amazetv.dts
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi
arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts
arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
arch/arm64/boot/dts/amlogic/meson-gxm-rbox-pro.dts
arch/arm64/boot/dts/qcom/msm8996.dtsi
arch/arm64/boot/dts/renesas/r8a774a1.dtsi
arch/arm64/boot/dts/renesas/r8a7796.dtsi
arch/arm64/boot/dts/renesas/r8a77965.dtsi
arch/arm64/kernel/hibernate.c
arch/arm64/kernel/hyp-stub.S
arch/arm64/kernel/kaslr.c
arch/arm64/kernel/machine_kexec_file.c
arch/arm64/kernel/probes/kprobes.c
arch/arm64/mm/dump.c
arch/arm64/mm/flush.c
arch/c6x/include/asm/Kbuild
arch/c6x/include/uapi/asm/Kbuild
arch/h8300/include/asm/Kbuild
arch/h8300/include/uapi/asm/Kbuild
arch/hexagon/include/asm/Kbuild
arch/hexagon/include/uapi/asm/Kbuild
arch/ia64/include/uapi/asm/Kbuild
arch/ia64/include/uapi/asm/socket.h [deleted file]
arch/m68k/coldfire/m5272.c
arch/m68k/emu/nfblock.c
arch/m68k/include/asm/Kbuild
arch/m68k/include/uapi/asm/Kbuild
arch/microblaze/include/asm/Kbuild
arch/microblaze/include/uapi/asm/Kbuild
arch/mips/Kconfig
arch/mips/ar7/platform.c
arch/mips/bcm47xx/setup.c
arch/mips/boot/dts/ingenic/ci20.dts
arch/mips/boot/dts/ingenic/jz4740.dtsi
arch/mips/boot/dts/xilfpga/nexys4ddr.dts
arch/mips/include/asm/atomic.h
arch/mips/include/asm/barrier.h
arch/mips/include/asm/bitops.h
arch/mips/include/asm/futex.h
arch/mips/include/asm/pgtable.h
arch/mips/include/uapi/asm/socket.h
arch/mips/kernel/mips-cm.c
arch/mips/kernel/process.c
arch/mips/loongson64/Platform
arch/mips/loongson64/common/reset.c
arch/mips/mm/tlbex.c
arch/mips/pci/pci-octeon.c
arch/mips/vdso/Makefile
arch/openrisc/include/asm/Kbuild
arch/openrisc/include/uapi/asm/Kbuild
arch/parisc/include/uapi/asm/socket.h
arch/powerpc/include/asm/book3s/64/pgtable.h
arch/powerpc/include/uapi/asm/socket.h
arch/powerpc/mm/pgtable-book3s64.c
arch/powerpc/net/bpf_jit_comp64.c
arch/powerpc/platforms/pseries/papr_scm.c
arch/riscv/Kconfig
arch/riscv/Makefile
arch/riscv/configs/defconfig
arch/riscv/include/asm/page.h
arch/riscv/include/asm/pgtable-bits.h
arch/riscv/include/asm/pgtable.h
arch/riscv/include/asm/processor.h
arch/riscv/kernel/asm-offsets.c
arch/riscv/kernel/entry.S
arch/riscv/kernel/setup.c
arch/riscv/kernel/smpboot.c
arch/riscv/kernel/vmlinux.lds.S
arch/riscv/mm/init.c
arch/riscv/net/Makefile [new file with mode: 0644]
arch/riscv/net/bpf_jit_comp.c [new file with mode: 0644]
arch/s390/include/asm/pnet.h
arch/s390/include/uapi/asm/Kbuild
arch/s390/include/uapi/asm/socket.h [deleted file]
arch/s390/kernel/swsusp.S
arch/s390/net/bpf_jit_comp.c
arch/s390/net/pnet.c
arch/s390/pci/pci.c
arch/sparc/include/uapi/asm/posix_types.h
arch/sparc/include/uapi/asm/socket.h
arch/unicore32/include/asm/Kbuild
arch/unicore32/include/uapi/asm/Kbuild
arch/x86/Kconfig
arch/x86/boot/compressed/head_64.S
arch/x86/boot/compressed/pgtable.h
arch/x86/entry/entry_64_compat.S
arch/x86/events/intel/core.c
arch/x86/events/intel/uncore_snbep.c
arch/x86/include/asm/intel-family.h
arch/x86/include/asm/mmu_context.h
arch/x86/include/asm/page_64_types.h
arch/x86/include/asm/pgtable.h
arch/x86/include/asm/resctrl_sched.h
arch/x86/include/uapi/asm/Kbuild
arch/x86/include/uapi/asm/socket.h [deleted file]
arch/x86/kernel/cpu/Makefile
arch/x86/kernel/cpu/bugs.c
arch/x86/kernel/cpu/mce/core.c
arch/x86/kernel/cpu/microcode/amd.c
arch/x86/kernel/cpu/resctrl/Makefile
arch/x86/kernel/crash.c
arch/x86/kernel/hpet.c
arch/x86/kernel/kexec-bzimage64.c
arch/x86/kernel/tsc.c
arch/x86/kvm/vmx/nested.c
arch/x86/kvm/vmx/vmx.c
arch/x86/kvm/x86.c
arch/x86/lib/iomem.c
arch/x86/lib/kaslr.c
arch/x86/mm/fault.c
arch/x86/mm/mem_encrypt_identity.c
arch/x86/mm/pageattr.c
arch/xtensa/Kconfig
arch/xtensa/boot/dts/Makefile
arch/xtensa/configs/audio_kc705_defconfig
arch/xtensa/configs/cadence_csp_defconfig
arch/xtensa/configs/generic_kc705_defconfig
arch/xtensa/configs/nommu_kc705_defconfig
arch/xtensa/configs/smp_lx200_defconfig
arch/xtensa/include/asm/Kbuild
arch/xtensa/include/uapi/asm/Kbuild
arch/xtensa/include/uapi/asm/socket.h [deleted file]
arch/xtensa/kernel/head.S
arch/xtensa/kernel/smp.c
arch/xtensa/kernel/time.c
block/blk-core.c
block/blk-flush.c
block/blk-iolatency.c
block/blk-merge.c
block/blk-mq-debugfs.c
block/blk-mq.c
block/blk-mq.h
drivers/acpi/bus.c
drivers/android/binder.c
drivers/android/binder_internal.h
drivers/android/binderfs.c
drivers/ata/libata-core.c
drivers/base/cacheinfo.c
drivers/base/power/runtime.c
drivers/bcma/bcma_private.h
drivers/bcma/driver_gpio.c
drivers/bcma/host_pci.c
drivers/bcma/host_soc.c
drivers/bcma/main.c
drivers/block/floppy.c
drivers/clk/clk.c
drivers/clk/imx/clk-frac-pll.c
drivers/clk/mmp/clk-of-mmp2.c
drivers/clk/qcom/gcc-sdm845.c
drivers/clk/ti/divider.c
drivers/cpuidle/poll_state.c
drivers/crypto/cavium/nitrox/nitrox_reqmgr.c
drivers/crypto/ccree/cc_driver.c
drivers/crypto/ccree/cc_pm.c
drivers/crypto/ccree/cc_pm.h
drivers/dma/at_xdmac.c
drivers/dma/bcm2835-dma.c
drivers/dma/dmatest.c
drivers/dma/imx-dma.c
drivers/edac/altera_edac.h
drivers/firmware/arm_scmi/bus.c
drivers/firmware/efi/arm-runtime.c
drivers/fpga/stratix10-soc.c
drivers/gpio/gpio-altera-a10sr.c
drivers/gpio/gpio-eic-sprd.c
drivers/gpio/gpio-pcf857x.c
drivers/gpio/gpio-vf610.c
drivers/gpio/gpiolib.c
drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c
drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
drivers/gpu/drm/amd/amdgpu/nbio_v7_4.c
drivers/gpu/drm/amd/amdgpu/psp_v11_0.c
drivers/gpu/drm/amd/amdgpu/soc15.c
drivers/gpu/drm/amd/amdkfd/kfd_crat.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
drivers/gpu/drm/amd/display/dc/dce/dce_clk_mgr.c
drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c
drivers/gpu/drm/drm_lease.c
drivers/gpu/drm/drm_modes.c
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_pmu.c
drivers/gpu/drm/i915/i915_pmu.h
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_fbdev.c
drivers/gpu/drm/i915/intel_opregion.c
drivers/gpu/drm/i915/intel_ringbuffer.h
drivers/gpu/drm/i915/intel_sprite.c
drivers/gpu/drm/imx/imx-ldb.c
drivers/gpu/drm/imx/ipuv3-plane.c
drivers/gpu/drm/omapdrm/dss/dsi.c
drivers/gpu/drm/radeon/ci_dpm.c
drivers/gpu/drm/radeon/si_dpm.c
drivers/gpu/drm/rockchip/rockchip_rgb.c
drivers/gpu/drm/rockchip/rockchip_rgb.h
drivers/gpu/drm/scheduler/sched_entity.c
drivers/gpu/drm/sun4i/sun4i_tcon.c
drivers/gpu/drm/vkms/vkms_crc.c
drivers/gpu/drm/vkms/vkms_crtc.c
drivers/gpu/drm/vkms/vkms_drv.c
drivers/gpu/drm/vkms/vkms_drv.h
drivers/gpu/drm/vkms/vkms_gem.c
drivers/gpu/drm/vkms/vkms_output.c
drivers/gpu/drm/vkms/vkms_plane.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/gpu/ipu-v3/ipu-common.c
drivers/gpu/ipu-v3/ipu-pre.c
drivers/hid/hid-debug.c
drivers/hwmon/nct6775.c
drivers/i2c/busses/i2c-omap.c
drivers/i3c/master.c
drivers/i3c/master/dw-i3c-master.c
drivers/ide/ide-atapi.c
drivers/ide/ide-io.c
drivers/ide/ide-park.c
drivers/ide/ide-probe.c
drivers/iio/adc/axp288_adc.c
drivers/iio/adc/ti-ads8688.c
drivers/iio/chemical/atlas-ph-sensor.c
drivers/infiniband/core/core_priv.h
drivers/infiniband/core/device.c
drivers/infiniband/core/umem_odp.c
drivers/infiniband/core/uverbs_main.c
drivers/infiniband/core/uverbs_std_types_device.c
drivers/infiniband/hw/hfi1/file_ops.c
drivers/infiniband/hw/hfi1/ud.c
drivers/infiniband/hw/hns/hns_roce_srq.c
drivers/infiniband/hw/mlx4/mad.c
drivers/infiniband/hw/mlx5/cmd.c
drivers/infiniband/hw/mlx5/cmd.h
drivers/infiniband/hw/mlx5/flow.c
drivers/infiniband/hw/mlx5/ib_rep.c
drivers/infiniband/hw/mlx5/ib_rep.h
drivers/infiniband/hw/mlx5/mad.c
drivers/infiniband/hw/mlx5/main.c
drivers/infiniband/hw/mlx5/mlx5_ib.h
drivers/infiniband/hw/mlx5/mr.c
drivers/infiniband/hw/mlx5/odp.c
drivers/infiniband/hw/mlx5/qp.c
drivers/infiniband/hw/qib/qib_ud.c
drivers/infiniband/sw/rdmavt/qp.c
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_cm.c
drivers/input/serio/olpc_apsp.c
drivers/iommu/amd_iommu.c
drivers/iommu/intel-iommu.c
drivers/iommu/mtk_iommu_v1.c
drivers/irqchip/irq-gic-v3-its.c
drivers/irqchip/irq-gic-v3-mbi.c
drivers/irqchip/irq-madera.c
drivers/irqchip/irq-mmp.c
drivers/irqchip/irq-stm32-exti.c
drivers/irqchip/irq-xtensa-mx.c
drivers/irqchip/irq-xtensa-pic.c
drivers/isdn/gigaset/ser-gigaset.c
drivers/isdn/hisax/hfc_pci.c
drivers/isdn/i4l/isdn_tty.c
drivers/isdn/i4l/isdn_v110.c
drivers/isdn/mISDN/socket.c
drivers/isdn/mISDN/timerdev.c
drivers/md/dm-crypt.c
drivers/md/dm-rq.c
drivers/md/dm-thin.c
drivers/md/dm.c
drivers/md/raid1.c
drivers/md/raid5-cache.c
drivers/md/raid5.c
drivers/mfd/Kconfig
drivers/misc/mei/client.c
drivers/misc/mei/hw-me-regs.h
drivers/misc/mei/pci-me.c
drivers/misc/mic/vop/vop_main.c
drivers/mmc/core/block.c
drivers/mmc/host/bcm2835.c
drivers/mmc/host/meson-gx-mmc.c
drivers/mmc/host/mtk-sd.c
drivers/mmc/host/sunxi-mmc.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c
drivers/mtd/nand/raw/nand_base.c
drivers/mtd/nand/raw/nand_bbt.c
drivers/mtd/nand/spi/core.c
drivers/net/Kconfig
drivers/net/appletalk/cops.c
drivers/net/bonding/bond_options.c
drivers/net/caif/caif_serial.c
drivers/net/caif/caif_spi.c
drivers/net/dsa/b53/b53_srab.c
drivers/net/dsa/bcm_sf2.c
drivers/net/dsa/bcm_sf2.h
drivers/net/dsa/bcm_sf2_cfp.c
drivers/net/dsa/bcm_sf2_regs.h
drivers/net/dsa/dsa_loop.c
drivers/net/dsa/microchip/ksz9477.c
drivers/net/dsa/mt7530.c
drivers/net/dsa/mt7530.h
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/mv88e6xxx/global1_atu.c
drivers/net/dsa/mv88e6xxx/serdes.c
drivers/net/ethernet/3com/3c515.c
drivers/net/ethernet/3com/3c59x.c
drivers/net/ethernet/adaptec/starfire.c
drivers/net/ethernet/alteon/acenic.c
drivers/net/ethernet/altera/altera_msgdma.c
drivers/net/ethernet/amazon/ena/ena_netdev.c
drivers/net/ethernet/amazon/ena/ena_netdev.h
drivers/net/ethernet/amd/amd8111e.c
drivers/net/ethernet/amd/au1000_eth.c
drivers/net/ethernet/amd/lance.c
drivers/net/ethernet/amd/ni65.c
drivers/net/ethernet/apple/bmac.c
drivers/net/ethernet/apple/mace.c
drivers/net/ethernet/arc/emac_main.c
drivers/net/ethernet/atheros/atl1c/atl1c_main.c
drivers/net/ethernet/atheros/atl1e/atl1e_main.c
drivers/net/ethernet/atheros/atlx/atl1.c
drivers/net/ethernet/atheros/atlx/atl2.c
drivers/net/ethernet/broadcom/b44.c
drivers/net/ethernet/broadcom/bcmsysport.c
drivers/net/ethernet/broadcom/bcmsysport.h
drivers/net/ethernet/broadcom/bgmac.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.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_tc.c
drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c
drivers/net/ethernet/broadcom/genet/bcmmii.c
drivers/net/ethernet/broadcom/sb1250-mac.c
drivers/net/ethernet/brocade/bna/bfa_ioc.c
drivers/net/ethernet/cadence/macb.h
drivers/net/ethernet/cadence/macb_main.c
drivers/net/ethernet/cavium/Kconfig
drivers/net/ethernet/cavium/liquidio/lio_core.c
drivers/net/ethernet/cavium/liquidio/lio_main.c
drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c
drivers/net/ethernet/chelsio/cxgb/sge.c
drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c
drivers/net/ethernet/chelsio/cxgb4/sched.c
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/smt.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h
drivers/net/ethernet/chelsio/cxgb4/t4_values.h
drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h
drivers/net/ethernet/chelsio/cxgb4vf/adapter.h
drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
drivers/net/ethernet/chelsio/cxgb4vf/sge.c
drivers/net/ethernet/cisco/enic/enic_main.c
drivers/net/ethernet/dec/tulip/de2104x.c
drivers/net/ethernet/dec/tulip/eeprom.c
drivers/net/ethernet/dlink/dl2k.c
drivers/net/ethernet/dlink/sundance.c
drivers/net/ethernet/fealnx.c
drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
drivers/net/ethernet/freescale/enetc/Kconfig
drivers/net/ethernet/freescale/enetc/Makefile
drivers/net/ethernet/freescale/enetc/enetc_hw.h
drivers/net/ethernet/freescale/enetc/enetc_ptp.c [new file with mode: 0644]
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/freescale/fec_mpc52xx.c
drivers/net/ethernet/freescale/gianfar_ethtool.c
drivers/net/ethernet/freescale/ucc_geth.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
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/hnae3.c
drivers/net/ethernet/hisilicon/hns3/hnae3.h
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c
drivers/net/ethernet/hisilicon/hns_mdio.c
drivers/net/ethernet/i825xx/82596.c
drivers/net/ethernet/i825xx/lib82596.c
drivers/net/ethernet/ibm/emac/Kconfig
drivers/net/ethernet/ibm/emac/core.c
drivers/net/ethernet/ibm/emac/core.h
drivers/net/ethernet/intel/e1000e/80003es2lan.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/fm10k/fm10k_main.c
drivers/net/ethernet/intel/fm10k/fm10k_pf.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_xsk.c
drivers/net/ethernet/intel/i40e/i40e_xsk.h
drivers/net/ethernet/intel/iavf/iavf_main.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igc/Makefile
drivers/net/ethernet/intel/igc/igc.h
drivers/net/ethernet/intel/igc/igc_base.c
drivers/net/ethernet/intel/igc/igc_base.h
drivers/net/ethernet/intel/igc/igc_defines.h
drivers/net/ethernet/intel/igc/igc_ethtool.c [new file with mode: 0644]
drivers/net/ethernet/intel/igc/igc_hw.h
drivers/net/ethernet/intel/igc/igc_main.c
drivers/net/ethernet/intel/igc/igc_phy.c
drivers/net/ethernet/intel/igc/igc_regs.h
drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_txrx_common.h
drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c
drivers/net/ethernet/lantiq_etop.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/mvpp2/mvpp2.h
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
drivers/net/ethernet/marvell/pxa168_eth.c
drivers/net/ethernet/marvell/skge.c
drivers/net/ethernet/mediatek/Kconfig
drivers/net/ethernet/mediatek/mtk_eth_soc.c
drivers/net/ethernet/mediatek/mtk_eth_soc.h
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx5/core/Makefile
drivers/net/ethernet/mellanox/mlx5/core/cmd.c
drivers/net/ethernet/mellanox/mlx5/core/ecpf.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/ecpf.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en/port.c
drivers/net/ethernet/mellanox/mlx5/core/en/port.h
drivers/net/ethernet/mellanox/mlx5/core/en/reporter.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c
drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h
drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.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_rep.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/eq.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/health.c
drivers/net/ethernet/mellanox/mlx5/core/lag.c
drivers/net/ethernet/mellanox/mlx5/core/mad.c [deleted file]
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
drivers/net/ethernet/mellanox/mlx5/core/mr.c
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/mellanox/mlx5/core/port.c
drivers/net/ethernet/mellanox/mlx5/core/qp.c
drivers/net/ethernet/mellanox/mlx5/core/sriov.c
drivers/net/ethernet/mellanox/mlx5/core/vport.c
drivers/net/ethernet/mellanox/mlxsw/Makefile
drivers/net/ethernet/mellanox/mlxsw/core.c
drivers/net/ethernet/mellanox/mlxsw/core.h
drivers/net/ethernet/mellanox/mlxsw/core_env.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxsw/core_env.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
drivers/net/ethernet/mellanox/mlxsw/i2c.c
drivers/net/ethernet/mellanox/mlxsw/reg.h
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
drivers/net/ethernet/mellanox/mlxsw/spectrum.h
drivers/net/ethernet/mellanox/mlxsw/spectrum1_acl_tcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum2_acl_tcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_atcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_bloom_filter.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
drivers/net/ethernet/mellanox/mlxsw/switchx2.c
drivers/net/ethernet/micrel/ks8695net.c
drivers/net/ethernet/moxa/moxart_ether.c
drivers/net/ethernet/moxa/moxart_ether.h
drivers/net/ethernet/mscc/ocelot.c
drivers/net/ethernet/myricom/myri10ge/myri10ge.c
drivers/net/ethernet/natsemi/natsemi.c
drivers/net/ethernet/natsemi/ns83820.c
drivers/net/ethernet/natsemi/sonic.c
drivers/net/ethernet/neterion/s2io.c
drivers/net/ethernet/neterion/vxge/vxge-main.c
drivers/net/ethernet/netronome/nfp/bpf/jit.c
drivers/net/ethernet/netronome/nfp/bpf/main.c
drivers/net/ethernet/netronome/nfp/bpf/offload.c
drivers/net/ethernet/netronome/nfp/flower/action.c
drivers/net/ethernet/netronome/nfp/flower/cmsg.c
drivers/net/ethernet/netronome/nfp/flower/match.c
drivers/net/ethernet/netronome/nfp/flower/offload.c
drivers/net/ethernet/netronome/nfp/nfp_devlink.c
drivers/net/ethernet/netronome/nfp/nfp_main.c
drivers/net/ethernet/netronome/nfp/nfp_main.h
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
drivers/net/ethernet/netronome/nfp/nfp_port.c
drivers/net/ethernet/netronome/nfp/nfp_port.h
drivers/net/ethernet/netronome/nfp/nfp_shared_buf.c
drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c
drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h
drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c
drivers/net/ethernet/ni/nixge.c
drivers/net/ethernet/nuvoton/w90p910_ether.c
drivers/net/ethernet/packetengines/hamachi.c
drivers/net/ethernet/packetengines/yellowfin.c
drivers/net/ethernet/qlogic/qed/qed.h
drivers/net/ethernet/qlogic/qed/qed_cxt.c
drivers/net/ethernet/qlogic/qed/qed_dev.c
drivers/net/ethernet/qlogic/qed/qed_hsi.h
drivers/net/ethernet/qlogic/qed/qed_l2.c
drivers/net/ethernet/qlogic/qed/qed_l2.h
drivers/net/ethernet/qlogic/qed/qed_ll2.c
drivers/net/ethernet/qlogic/qed/qed_main.c
drivers/net/ethernet/qlogic/qed/qed_mcp.c
drivers/net/ethernet/qlogic/qed/qed_mcp.h
drivers/net/ethernet/qlogic/qed/qed_sp.h
drivers/net/ethernet/qlogic/qed/qed_sp_commands.c
drivers/net/ethernet/qlogic/qed/qed_spq.c
drivers/net/ethernet/qlogic/qed/qed_sriov.c
drivers/net/ethernet/qlogic/qed/qed_vf.c
drivers/net/ethernet/qlogic/qede/qede.h
drivers/net/ethernet/qlogic/qede/qede_ethtool.c
drivers/net/ethernet/qlogic/qede/qede_filter.c
drivers/net/ethernet/qlogic/qede/qede_fp.c
drivers/net/ethernet/qlogic/qede/qede_main.c
drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c
drivers/net/ethernet/qlogic/qlge/qlge_main.c
drivers/net/ethernet/qualcomm/emac/emac-mac.c
drivers/net/ethernet/realtek/8139cp.c
drivers/net/ethernet/realtek/r8169.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/renesas/sh_eth.h
drivers/net/ethernet/rocker/rocker.h
drivers/net/ethernet/rocker/rocker_main.c
drivers/net/ethernet/rocker/rocker_ofdpa.c
drivers/net/ethernet/sfc/ef10.c
drivers/net/ethernet/sfc/efx.c
drivers/net/ethernet/sfc/mcdi_pcol.h
drivers/net/ethernet/sfc/rx.c
drivers/net/ethernet/sfc/tx.c
drivers/net/ethernet/sgi/ioc3-eth.c
drivers/net/ethernet/sgi/meth.c
drivers/net/ethernet/sis/sis190.c
drivers/net/ethernet/sis/sis900.c
drivers/net/ethernet/smsc/epic100.c
drivers/net/ethernet/smsc/smc911x.c
drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
drivers/net/ethernet/sun/cassini.c
drivers/net/ethernet/sun/sunbmac.c
drivers/net/ethernet/sun/sunhme.c
drivers/net/ethernet/tehuti/tehuti.c
drivers/net/ethernet/ti/cpmac.c
drivers/net/ethernet/via/via-velocity.c
drivers/net/ethernet/xilinx/ll_temac_main.c
drivers/net/ethernet/xilinx/xilinx_axienet_main.c
drivers/net/ethernet/xilinx/xilinx_emaclite.c
drivers/net/ethernet/xscale/ixp4xx_eth.c
drivers/net/fddi/defxx.c
drivers/net/fddi/skfp/pcmplc.c
drivers/net/geneve.c
drivers/net/ieee802154/mcr20a.c
drivers/net/ipvlan/Makefile
drivers/net/ipvlan/ipvlan.h
drivers/net/ipvlan/ipvlan_core.c
drivers/net/ipvlan/ipvlan_l3s.c [new file with mode: 0644]
drivers/net/ipvlan/ipvlan_main.c
drivers/net/macvlan.c
drivers/net/netdevsim/bpf.c
drivers/net/netdevsim/netdev.c
drivers/net/phy/aquantia.c
drivers/net/phy/dp83640.c
drivers/net/phy/dp83867.c
drivers/net/phy/dp83tc811.c
drivers/net/phy/fixed_phy.c
drivers/net/phy/marvell.c
drivers/net/phy/marvell10g.c
drivers/net/phy/phy-c45.c
drivers/net/phy/phy-core.c
drivers/net/phy/phy.c
drivers/net/phy/phy_device.c
drivers/net/phy/phylink.c
drivers/net/phy/realtek.c
drivers/net/phy/sfp-bus.c
drivers/net/phy/sfp.c
drivers/net/phy/sfp.h
drivers/net/team/team.c
drivers/net/tun.c
drivers/net/usb/cdc-phonet.c
drivers/net/usb/lan78xx.c
drivers/net/usb/pegasus.c
drivers/net/usb/rtl8150.c
drivers/net/veth.c
drivers/net/virtio_net.c
drivers/net/vxlan.c
drivers/net/wan/dscc4.c
drivers/net/wan/fsl_ucc_hdlc.c
drivers/net/wan/wanxl.c
drivers/net/wimax/i2400m/rx.c
drivers/net/wimax/i2400m/usb.c
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/broadcom/b43/debugfs.c
drivers/net/wireless/broadcom/b43legacy/debugfs.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c
drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h
drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c
drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile
drivers/net/wireless/intel/iwlegacy/3945-mac.c
drivers/net/wireless/intel/iwlegacy/4965-mac.c
drivers/net/wireless/intel/iwlegacy/common.h
drivers/net/wireless/intel/iwlegacy/debug.c
drivers/net/wireless/intel/iwlwifi/Kconfig
drivers/net/wireless/intel/iwlwifi/cfg/22000.c
drivers/net/wireless/intel/iwlwifi/cfg/9000.c
drivers/net/wireless/intel/iwlwifi/dvm/Makefile
drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
drivers/net/wireless/intel/iwlwifi/dvm/main.c
drivers/net/wireless/intel/iwlwifi/dvm/tt.c
drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
drivers/net/wireless/intel/iwlwifi/fw/api/d3.h
drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
drivers/net/wireless/intel/iwlwifi/fw/api/debug.h
drivers/net/wireless/intel/iwlwifi/fw/api/location.h [new file with mode: 0644]
drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h
drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h
drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h
drivers/net/wireless/intel/iwlwifi/fw/api/tof.h [deleted file]
drivers/net/wireless/intel/iwlwifi/fw/dbg.c
drivers/net/wireless/intel/iwlwifi/fw/dbg.h
drivers/net/wireless/intel/iwlwifi/fw/error-dump.h
drivers/net/wireless/intel/iwlwifi/fw/file.h
drivers/net/wireless/intel/iwlwifi/fw/runtime.h
drivers/net/wireless/intel/iwlwifi/iwl-config.h
drivers/net/wireless/intel/iwlwifi/iwl-csr.h
drivers/net/wireless/intel/iwlwifi/iwl-drv.c
drivers/net/wireless/intel/iwlwifi/iwl-io.c
drivers/net/wireless/intel/iwlwifi/iwl-io.h
drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/intel/iwlwifi/iwl-prph.h
drivers/net/wireless/intel/iwlwifi/mvm/Makefile
drivers/net/wireless/intel/iwlwifi/mvm/d3.c
drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c
drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
drivers/net/wireless/intel/iwlwifi/mvm/fw.c
drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
drivers/net/wireless/intel/iwlwifi/mvm/ops.c
drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c
drivers/net/wireless/intel/iwlwifi/mvm/rs.c
drivers/net/wireless/intel/iwlwifi/mvm/rx.c
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
drivers/net/wireless/intel/iwlwifi/mvm/sf.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.h
drivers/net/wireless/intel/iwlwifi/mvm/tdls.c
drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
drivers/net/wireless/intel/iwlwifi/mvm/tof.c [deleted file]
drivers/net/wireless/intel/iwlwifi/mvm/tof.h [deleted file]
drivers/net/wireless/intel/iwlwifi/mvm/tx.c
drivers/net/wireless/intel/iwlwifi/mvm/utils.c
drivers/net/wireless/intel/iwlwifi/pcie/drv.c
drivers/net/wireless/intel/iwlwifi/pcie/internal.h
drivers/net/wireless/intel/iwlwifi/pcie/rx.c
drivers/net/wireless/intel/iwlwifi/pcie/trans.c
drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
drivers/net/wireless/intel/iwlwifi/pcie/tx.c
drivers/net/wireless/marvell/libertas/debugfs.c
drivers/net/wireless/marvell/libertas/mesh.c
drivers/net/wireless/marvell/libertas_tf/main.c
drivers/net/wireless/marvell/mwifiex/Kconfig
drivers/net/wireless/marvell/mwifiex/debugfs.c
drivers/net/wireless/marvell/mwifiex/sdio.c
drivers/net/wireless/marvell/mwifiex/sdio.h
drivers/net/wireless/mediatek/mt76/dma.c
drivers/net/wireless/mediatek/mt76/mac80211.c
drivers/net/wireless/mediatek/mt76/mt76.h
drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c
drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h
drivers/net/wireless/mediatek/mt76/mt76x0/initvals.h
drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
drivers/net/wireless/mediatek/mt76/mt76x0/phy.c
drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
drivers/net/wireless/mediatek/mt76/mt76x0/usb_mcu.c
drivers/net/wireless/mediatek/mt76/mt76x02.h
drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c
drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
drivers/net/wireless/mediatek/mt76/mt76x02_phy.c
drivers/net/wireless/mediatek/mt76/mt76x02_phy.h
drivers/net/wireless/mediatek/mt76/mt76x02_regs.h
drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c
drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
drivers/net/wireless/mediatek/mt76/mt76x02_util.c
drivers/net/wireless/mediatek/mt76/mt76x2/init.c
drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h
drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h
drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c
drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c
drivers/net/wireless/mediatek/mt76/mt76x2/phy.c
drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c
drivers/net/wireless/mediatek/mt76/tx.c
drivers/net/wireless/mediatek/mt76/usb.c
drivers/net/wireless/mediatek/mt76/util.c
drivers/net/wireless/mediatek/mt7601u/dma.c
drivers/net/wireless/mediatek/mt7601u/eeprom.h
drivers/net/wireless/quantenna/Makefile
drivers/net/wireless/quantenna/qtnfmac/bus.h
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
drivers/net/wireless/quantenna/qtnfmac/cfg80211.h
drivers/net/wireless/quantenna/qtnfmac/commands.c
drivers/net/wireless/quantenna/qtnfmac/commands.h
drivers/net/wireless/quantenna/qtnfmac/core.c
drivers/net/wireless/quantenna/qtnfmac/core.h
drivers/net/wireless/quantenna/qtnfmac/debug.c
drivers/net/wireless/quantenna/qtnfmac/debug.h
drivers/net/wireless/quantenna/qtnfmac/event.c
drivers/net/wireless/quantenna/qtnfmac/event.h
drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
drivers/net/wireless/quantenna/qtnfmac/qlink.h
drivers/net/wireless/quantenna/qtnfmac/qlink_util.c
drivers/net/wireless/quantenna/qtnfmac/qlink_util.h
drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h
drivers/net/wireless/quantenna/qtnfmac/shm_ipc.c
drivers/net/wireless/quantenna/qtnfmac/shm_ipc.h
drivers/net/wireless/quantenna/qtnfmac/shm_ipc_defs.h
drivers/net/wireless/quantenna/qtnfmac/trans.c
drivers/net/wireless/quantenna/qtnfmac/trans.h
drivers/net/wireless/quantenna/qtnfmac/util.c
drivers/net/wireless/quantenna/qtnfmac/util.h
drivers/net/wireless/ralink/rt2x00/rt2800lib.c
drivers/net/wireless/ralink/rt2x00/rt2x00debug.c
drivers/net/wireless/ralink/rt2x00/rt61pci.c
drivers/net/wireless/ray_cs.c
drivers/net/wireless/realtek/rtl818x/rtl8180/Makefile
drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
drivers/net/wireless/realtek/rtl818x/rtl8187/Makefile
drivers/net/wireless/realtek/rtlwifi/base.c
drivers/net/wireless/realtek/rtlwifi/core.c
drivers/net/wireless/realtek/rtlwifi/debug.c
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/dm.c
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c
drivers/net/wireless/realtek/rtlwifi/rtl8723be/phy.c
drivers/net/wireless/rsi/rsi_91x_debugfs.c
drivers/net/wireless/rsi/rsi_91x_hal.c
drivers/net/wireless/rsi/rsi_91x_mac80211.c
drivers/net/wireless/rsi/rsi_91x_main.c
drivers/net/wireless/rsi/rsi_91x_mgmt.c
drivers/net/wireless/rsi/rsi_91x_sdio.c
drivers/net/wireless/rsi/rsi_main.h
drivers/net/wireless/rsi/rsi_mgmt.h
drivers/net/wireless/st/cw1200/debug.c
drivers/net/wireless/st/cw1200/fwio.c
drivers/net/wireless/st/cw1200/queue.c
drivers/net/wireless/st/cw1200/scan.c
drivers/net/wireless/ti/wl1251/debugfs.c
drivers/net/wireless/ti/wl12xx/debugfs.c
drivers/net/wireless/ti/wl18xx/debugfs.c
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/debugfs.c
drivers/net/wireless/ti/wlcore/debugfs.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/sdio.c
drivers/net/wireless/virt_wifi.c
drivers/net/xen-netback/xenbus.c
drivers/nvme/host/core.c
drivers/nvme/host/nvme.h
drivers/nvme/host/pci.c
drivers/of/of_mdio.c
drivers/pci/controller/dwc/pci-imx6.c
drivers/pci/controller/dwc/pcie-armada8k.c
drivers/pci/quirks.c
drivers/phy/marvell/Kconfig
drivers/phy/marvell/Makefile
drivers/phy/marvell/phy-armada38x-comphy.c [new file with mode: 0644]
drivers/pinctrl/intel/pinctrl-cherryview.c
drivers/pinctrl/mediatek/Kconfig
drivers/pinctrl/pinctrl-mcp23s08.c
drivers/pinctrl/sunxi/pinctrl-sun50i-h6.c
drivers/pinctrl/sunxi/pinctrl-sunxi.c
drivers/pinctrl/sunxi/pinctrl-sunxi.h
drivers/platform/x86/Kconfig
drivers/ptp/Kconfig
drivers/ptp/ptp_qoriq.c
drivers/ptp/ptp_qoriq_debugfs.c
drivers/s390/block/dasd_eckd.c
drivers/s390/crypto/ap_bus.c
drivers/s390/net/Makefile
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_core_mpc.c
drivers/s390/net/qeth_core_mpc.h
drivers/s390/net/qeth_core_sys.c
drivers/s390/net/qeth_ethtool.c [new file with mode: 0644]
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c
drivers/s390/scsi/zfcp_aux.c
drivers/s390/scsi/zfcp_scsi.c
drivers/scsi/53c700.c
drivers/scsi/aic94xx/aic94xx_init.c
drivers/scsi/bnx2fc/bnx2fc_io.c
drivers/scsi/cxlflash/main.c
drivers/scsi/libfc/fc_lport.c
drivers/scsi/libfc/fc_rport.c
drivers/scsi/scsi_debug.c
drivers/scsi/sd_zbc.c
drivers/soc/fsl/qbman/qman.c
drivers/staging/fsl-dpaa2/ethsw/ethsw.c
drivers/staging/octeon/ethernet-mdio.c
drivers/staging/speakup/spk_ttyio.c
drivers/target/target_core_configfs.c
drivers/thermal/cpu_cooling.c
drivers/thermal/of-thermal.c
drivers/tty/serial/8250/8250_mtk.c
drivers/tty/serial/8250/8250_pci.c
drivers/tty/serial/earlycon-riscv-sbi.c
drivers/tty/serial/serial_core.c
drivers/tty/serial/sh-sci.c
drivers/usb/dwc3/dwc3-exynos.c
drivers/usb/dwc3/gadget.c
drivers/usb/gadget/udc/net2272.c
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/musbhsdma.c
drivers/usb/phy/Kconfig
drivers/usb/phy/phy-am335x.c
drivers/usb/typec/tcpm/tcpm.c
drivers/vhost/net.c
drivers/vhost/scsi.c
drivers/vhost/vhost.c
drivers/vhost/vhost.h
drivers/vhost/vsock.c
drivers/virtio/virtio_ring.c
fs/aio.c
fs/autofs/expire.c
fs/autofs/inode.c
fs/binfmt_script.c
fs/btrfs/ctree.c
fs/btrfs/super.c
fs/btrfs/transaction.c
fs/btrfs/volumes.c
fs/buffer.c
fs/cifs/cifsfs.h
fs/cifs/file.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2pdu.h
fs/dcache.c
fs/debugfs/inode.c
fs/dlm/lowcomms.c
fs/drop_caches.c
fs/ext4/fsync.c
fs/fuse/dev.c
fs/fuse/file.c
fs/fuse/inode.c
fs/gfs2/glops.c
fs/gfs2/log.c
fs/gfs2/lops.c
fs/gfs2/lops.h
fs/gfs2/ops_fstype.c
fs/gfs2/recovery.c
fs/gfs2/recovery.h
fs/gfs2/rgrp.c
fs/gfs2/super.c
fs/inode.c
fs/iomap.c
fs/nfs/super.c
fs/nfs/write.c
fs/nfsd/vfs.c
fs/proc/generic.c
fs/proc/internal.h
fs/proc/proc_net.c
fs/proc/task_mmu.c
fs/xfs/scrub/repair.c
fs/xfs/xfs_aops.c
fs/xfs/xfs_buf.c
include/asm-generic/shmparam.h [moved from include/uapi/asm-generic/shmparam.h with 100% similarity]
include/dt-bindings/clock/imx8mq-clock.h
include/dt-bindings/clock/marvell,mmp2.h
include/linux/bcma/bcma.h
include/linux/blktrace_api.h
include/linux/bpf-cgroup.h
include/linux/bpf.h
include/linux/bpf_types.h
include/linux/bpf_verifier.h
include/linux/btf.h
include/linux/cpu.h
include/linux/dcache.h
include/linux/ethtool.h
include/linux/filter.h
include/linux/fs.h
include/linux/fsl/ptp_qoriq.h
include/linux/hid-debug.h
include/linux/ide.h
include/linux/igmp.h
include/linux/interrupt.h
include/linux/irqchip/arm-gic-v3.h
include/linux/mdio.h
include/linux/memory_hotplug.h
include/linux/mlx5/device.h
include/linux/mlx5/driver.h
include/linux/mlx5/eswitch.h
include/linux/mlx5/mlx5_ifc.h
include/linux/mlx5/port.h
include/linux/mlx5/vport.h
include/linux/mm_types.h
include/linux/mmc/card.h
include/linux/netdevice.h
include/linux/objagg.h
include/linux/phy.h
include/linux/phy_fixed.h
include/linux/phylink.h
include/linux/pm_runtime.h
include/linux/ptr_ring.h
include/linux/qed/qed_if.h
include/linux/sched.h
include/linux/sched/coredump.h
include/linux/sched/wake_q.h
include/linux/signal.h
include/linux/skbuff.h
include/linux/socket.h
include/linux/stmmac.h
include/net/act_api.h
include/net/addrconf.h
include/net/cfg80211.h
include/net/devlink.h
include/net/flow_offload.h [new file with mode: 0644]
include/net/inetpeer.h
include/net/l3mdev.h
include/net/lwtunnel.h
include/net/mac80211.h
include/net/netfilter/nf_tables.h
include/net/netlink.h
include/net/pkt_cls.h
include/net/sch_generic.h
include/net/sctp/structs.h
include/net/sock.h
include/net/switchdev.h
include/net/tc_act/tc_csum.h
include/net/tc_act/tc_gact.h
include/net/tc_act/tc_mirred.h
include/net/tc_act/tc_pedit.h
include/net/tc_act/tc_sample.h
include/net/tc_act/tc_skbedit.h
include/net/tc_act/tc_tunnel_key.h
include/net/tc_act/tc_vlan.h
include/net/tls.h
include/rdma/ib_verbs.h
include/sound/compress_driver.h
include/sound/hda_codec.h
include/trace/events/devlink.h
include/trace/events/mlxsw.h [new file with mode: 0644]
include/trace/events/neigh.h [new file with mode: 0644]
include/uapi/asm-generic/socket.h
include/uapi/linux/batadv_packet.h
include/uapi/linux/batman_adv.h
include/uapi/linux/bpf.h
include/uapi/linux/devlink.h
include/uapi/linux/errqueue.h
include/uapi/linux/inet_diag.h
include/uapi/linux/mdio.h
include/uapi/linux/nl80211.h
include/uapi/linux/pkt_cls.h
include/uapi/linux/rds.h
include/uapi/linux/sctp.h
include/uapi/linux/tc_act/tc_bpf.h
include/uapi/linux/tc_act/tc_connmark.h
include/uapi/linux/tc_act/tc_csum.h
include/uapi/linux/tc_act/tc_gact.h
include/uapi/linux/tc_act/tc_ife.h
include/uapi/linux/tc_act/tc_ipt.h
include/uapi/linux/tc_act/tc_mirred.h
include/uapi/linux/tc_act/tc_nat.h
include/uapi/linux/tc_act/tc_pedit.h
include/uapi/linux/tc_act/tc_sample.h
include/uapi/linux/tc_act/tc_skbedit.h
include/uapi/linux/tc_act/tc_skbmod.h
include/uapi/linux/tc_act/tc_tunnel_key.h
include/uapi/linux/tc_act/tc_vlan.h
include/uapi/linux/time.h
include/uapi/linux/time_types.h [new file with mode: 0644]
include/uapi/linux/tls.h
include/uapi/linux/virtio_config.h
include/uapi/linux/virtio_ring.h
include/uapi/rdma/hns-abi.h
init/Kconfig
init/main.c
kernel/Kconfig.locks
kernel/bpf/arraymap.c
kernel/bpf/btf.c
kernel/bpf/cgroup.c
kernel/bpf/core.c
kernel/bpf/hashtab.c
kernel/bpf/helpers.c
kernel/bpf/local_storage.c
kernel/bpf/map_in_map.c
kernel/bpf/offload.c
kernel/bpf/percpu_freelist.c
kernel/bpf/percpu_freelist.h
kernel/bpf/syscall.c
kernel/bpf/verifier.c
kernel/cgroup/cgroup.c
kernel/cpu.c
kernel/events/core.c
kernel/events/ring_buffer.c
kernel/exit.c
kernel/futex.c
kernel/irq/irqdesc.c
kernel/irq/manage.c
kernel/locking/rtmutex.c
kernel/locking/rwsem-xadd.c
kernel/relay.c
kernel/sched/core.c
kernel/sched/fair.c
kernel/sched/psi.c
kernel/signal.c
kernel/smp.c
kernel/time/posix-cpu-timers.c
kernel/trace/bpf_trace.c
kernel/trace/trace_probe_tmpl.h
kernel/trace/trace_uprobe.c
kernel/workqueue.c
kernel/workqueue_internal.h
lib/objagg.c
lib/test_kmod.c
lib/test_objagg.c
lib/test_rhashtable.c
mm/gup.c
mm/hugetlb.c
mm/kasan/Makefile
mm/memory-failure.c
mm/memory_hotplug.c
mm/migrate.c
mm/oom_kill.c
mm/page_alloc.c
mm/page_ext.c
mm/vmscan.c
net/Kconfig
net/atm/proc.c
net/batman-adv/Kconfig
net/batman-adv/Makefile
net/batman-adv/bat_algo.c
net/batman-adv/bat_algo.h
net/batman-adv/bat_iv_ogm.c
net/batman-adv/bat_iv_ogm.h
net/batman-adv/bat_v.c
net/batman-adv/bat_v.h
net/batman-adv/bat_v_elp.c
net/batman-adv/bat_v_elp.h
net/batman-adv/bat_v_ogm.c
net/batman-adv/bat_v_ogm.h
net/batman-adv/bitarray.c
net/batman-adv/bitarray.h
net/batman-adv/bridge_loop_avoidance.c
net/batman-adv/bridge_loop_avoidance.h
net/batman-adv/debugfs.c
net/batman-adv/debugfs.h
net/batman-adv/distributed-arp-table.c
net/batman-adv/distributed-arp-table.h
net/batman-adv/fragmentation.c
net/batman-adv/fragmentation.h
net/batman-adv/gateway_client.c
net/batman-adv/gateway_client.h
net/batman-adv/gateway_common.c
net/batman-adv/gateway_common.h
net/batman-adv/hard-interface.c
net/batman-adv/hard-interface.h
net/batman-adv/hash.c
net/batman-adv/hash.h
net/batman-adv/icmp_socket.c
net/batman-adv/icmp_socket.h
net/batman-adv/log.c
net/batman-adv/log.h
net/batman-adv/main.c
net/batman-adv/main.h
net/batman-adv/multicast.c
net/batman-adv/multicast.h
net/batman-adv/netlink.c
net/batman-adv/netlink.h
net/batman-adv/network-coding.c
net/batman-adv/network-coding.h
net/batman-adv/originator.c
net/batman-adv/originator.h
net/batman-adv/routing.c
net/batman-adv/routing.h
net/batman-adv/send.c
net/batman-adv/send.h
net/batman-adv/soft-interface.c
net/batman-adv/soft-interface.h
net/batman-adv/sysfs.c
net/batman-adv/sysfs.h
net/batman-adv/tp_meter.c
net/batman-adv/tp_meter.h
net/batman-adv/trace.c
net/batman-adv/trace.h
net/batman-adv/translation-table.c
net/batman-adv/translation-table.h
net/batman-adv/tvlv.c
net/batman-adv/tvlv.h
net/batman-adv/types.h
net/bluetooth/hci_sock.c
net/bpfilter/Makefile
net/bpfilter/main.c
net/bridge/br_multicast.c
net/bridge/br_switchdev.c
net/bridge/netfilter/ebtables.c
net/caif/cfpkt_skbuff.c
net/compat.c
net/core/Makefile
net/core/dev.c
net/core/devlink.c
net/core/ethtool.c
net/core/filter.c
net/core/flow_offload.c [new file with mode: 0644]
net/core/lwt_bpf.c
net/core/neighbour.c
net/core/net-sysfs.c
net/core/net-traces.c
net/core/page_pool.c
net/core/rtnetlink.c
net/core/scm.c
net/core/skmsg.c
net/core/sock.c
net/dccp/ccid.h
net/decnet/dn_dev.c
net/dsa/dsa2.c
net/dsa/master.c
net/dsa/slave.c
net/dsa/tag_ksz.c
net/ipv4/fib_semantics.c
net/ipv4/igmp.c
net/ipv4/inet_diag.c
net/ipv4/inetpeer.c
net/ipv4/ip_gre.c
net/ipv4/ip_vti.c
net/ipv4/ipconfig.c
net/ipv4/ipmr.c
net/ipv4/netfilter/ipt_CLUSTERIP.c
net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
net/ipv4/netfilter/nf_nat_snmp_basic_main.c
net/ipv4/route.c
net/ipv4/tcp.c
net/ipv6/addrconf.c
net/ipv6/addrconf_core.c
net/ipv6/af_inet6.c
net/ipv6/ip6_gre.c
net/ipv6/ip6mr.c
net/ipv6/netfilter.c
net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
net/ipv6/route.c
net/ipv6/seg6.c
net/ipv6/seg6_iptunnel.c
net/ipv6/sit.c
net/l2tp/l2tp_core.c
net/l2tp/l2tp_core.h
net/l2tp/l2tp_ip.c
net/l2tp/l2tp_ip6.c
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/debugfs.c
net/mac80211/debugfs_sta.c
net/mac80211/driver-ops.h
net/mac80211/ht.c
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/mesh.h
net/mac80211/mesh_hwmp.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rc80211_minstrel_ht_debugfs.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/mpls/mpls_iptunnel.c
net/netfilter/ipvs/Kconfig
net/netfilter/ipvs/ip_vs_core.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_tables_api.c
net/netfilter/nfnetlink_osf.c
net/netfilter/nft_compat.c
net/netfilter/nft_dynset.c
net/netfilter/nft_immediate.c
net/netfilter/nft_lookup.c
net/netfilter/nft_objref.c
net/netfilter/x_tables.c
net/netrom/nr_timer.c
net/packet/af_packet.c
net/rds/af_rds.c
net/rds/bind.c
net/rds/connection.c
net/rds/ib.c
net/rds/ib.h
net/rds/ib_cm.c
net/rds/ib_recv.c
net/rds/ib_send.c
net/rds/rdma_transport.c
net/rds/rdma_transport.h
net/rds/rds.h
net/rds/recv.c
net/rds/send.c
net/rds/tcp.c
net/rds/tcp_listen.c
net/rds/threads.c
net/rose/rose_route.c
net/rxrpc/local_object.c
net/rxrpc/recvmsg.c
net/sched/act_api.c
net/sched/act_bpf.c
net/sched/act_connmark.c
net/sched/act_csum.c
net/sched/act_gact.c
net/sched/act_ife.c
net/sched/act_ipt.c
net/sched/act_mirred.c
net/sched/act_nat.c
net/sched/act_pedit.c
net/sched/act_police.c
net/sched/act_sample.c
net/sched/act_simple.c
net/sched/act_skbedit.c
net/sched/act_skbmod.c
net/sched/act_tunnel_key.c
net/sched/act_vlan.c
net/sched/cls_api.c
net/sched/cls_basic.c
net/sched/cls_bpf.c
net/sched/cls_cgroup.c
net/sched/cls_flow.c
net/sched/cls_flower.c
net/sched/cls_fw.c
net/sched/cls_matchall.c
net/sched/cls_route.c
net/sched/cls_rsvp.h
net/sched/cls_tcindex.c
net/sched/cls_u32.c
net/sched/sch_api.c
net/sched/sch_generic.c
net/sctp/associola.c
net/sctp/diag.c
net/sctp/offload.c
net/sctp/outqueue.c
net/sctp/socket.c
net/sctp/stream.c
net/smc/af_smc.c
net/smc/smc_cdc.c
net/smc/smc_cdc.h
net/smc/smc_clc.c
net/smc/smc_close.c
net/smc/smc_core.c
net/smc/smc_core.h
net/smc/smc_diag.c
net/smc/smc_ib.c
net/smc/smc_llc.c
net/smc/smc_pnet.c
net/smc/smc_tx.c
net/smc/smc_wr.c
net/smc/smc_wr.h
net/socket.c
net/sunrpc/xprtrdma/svc_rdma_sendto.c
net/sunrpc/xprtrdma/svc_rdma_transport.c
net/switchdev/switchdev.c
net/tipc/link.c
net/tipc/msg.h
net/tipc/node.c
net/tls/tls_device.c
net/tls/tls_device_fallback.c
net/tls/tls_main.c
net/tls/tls_sw.c
net/vmw_vsock/af_vsock.c
net/vmw_vsock/virtio_transport.c
net/vmw_vsock/vmci_transport.c
net/wireless/ap.c
net/wireless/core.c
net/wireless/core.h
net/wireless/nl80211.c
net/wireless/pmsr.c
net/wireless/reg.c
net/wireless/sme.c
net/wireless/util.c
net/wireless/wext-compat.c
net/x25/af_x25.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_user.c
samples/bpf/Makefile
samples/bpf/xdp1_user.c
samples/bpf/xdp_adjust_tail_user.c
samples/bpf/xdp_redirect_cpu_user.c
samples/bpf/xdp_redirect_map_user.c
samples/bpf/xdp_redirect_user.c
samples/bpf/xdp_router_ipv4_user.c
samples/bpf/xdp_rxq_info_user.c
samples/bpf/xdp_sample_pkts_user.c
samples/bpf/xdp_tx_iptunnel_user.c
samples/bpf/xdpsock_user.c
samples/mei/mei-amt-version.c
security/apparmor/domain.c
security/apparmor/lsm.c
sound/core/pcm_lib.c
sound/pci/hda/hda_bind.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_ca0132.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_realtek.c
sound/soc/codecs/hdmi-codec.c
sound/soc/codecs/rt5682.c
sound/soc/samsung/i2s.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/ssi.c
sound/soc/sh/rcar/ssiu.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-topology.c
sound/usb/pcm.c
sound/usb/quirks.c
tools/bpf/bpftool/Documentation/bpftool-cgroup.rst
tools/bpf/bpftool/Documentation/bpftool-feature.rst
tools/bpf/bpftool/Documentation/bpftool-prog.rst
tools/bpf/bpftool/Documentation/bpftool.rst
tools/bpf/bpftool/common.c
tools/bpf/bpftool/map.c
tools/bpf/bpftool/prog.c
tools/iio/iio_generic_buffer.c
tools/include/uapi/asm/bitsperlong.h
tools/include/uapi/linux/bpf.h
tools/include/uapi/linux/if_link.h
tools/include/uapi/linux/in.h
tools/include/uapi/linux/tc_act/tc_bpf.h
tools/lib/bpf/bpf.c
tools/lib/bpf/bpf.h
tools/lib/bpf/btf.c
tools/lib/bpf/btf.h
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf.h
tools/lib/bpf/libbpf.map
tools/lib/bpf/libbpf_util.h [new file with mode: 0644]
tools/lib/bpf/netlink.c
tools/lib/bpf/test_libbpf.cpp
tools/perf/Documentation/perf-c2c.txt
tools/perf/Documentation/perf-mem.txt
tools/perf/arch/powerpc/util/Build
tools/perf/arch/powerpc/util/mem-events.c [new file with mode: 0644]
tools/perf/builtin-script.c
tools/perf/builtin-trace.c
tools/perf/tests/attr.py
tools/perf/tests/evsel-tp-sched.c
tools/perf/ui/browsers/annotate.c
tools/perf/util/bpf-loader.c
tools/perf/util/c++/clang.cpp
tools/perf/util/cpumap.c
tools/perf/util/mem-events.c
tools/perf/util/ordered-events.c
tools/perf/util/setup.py
tools/perf/util/symbol-elf.c
tools/testing/selftests/Makefile
tools/testing/selftests/bpf/.gitignore
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/bpf_helpers.h
tools/testing/selftests/bpf/bpf_util.h
tools/testing/selftests/bpf/progs/bpf_flow.c [moved from tools/testing/selftests/bpf/bpf_flow.c with 100% similarity]
tools/testing/selftests/bpf/progs/connect4_prog.c [moved from tools/testing/selftests/bpf/connect4_prog.c with 100% similarity]
tools/testing/selftests/bpf/progs/connect6_prog.c [moved from tools/testing/selftests/bpf/connect6_prog.c with 100% similarity]
tools/testing/selftests/bpf/progs/dev_cgroup.c [moved from tools/testing/selftests/bpf/dev_cgroup.c with 100% similarity]
tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c [moved from tools/testing/selftests/bpf/get_cgroup_id_kern.c with 100% similarity]
tools/testing/selftests/bpf/progs/netcnt_prog.c [moved from tools/testing/selftests/bpf/netcnt_prog.c with 100% similarity]
tools/testing/selftests/bpf/progs/sample_map_ret0.c [moved from tools/testing/selftests/bpf/sample_map_ret0.c with 100% similarity]
tools/testing/selftests/bpf/progs/sample_ret0.c [moved from tools/testing/selftests/bpf/sample_ret0.c with 100% similarity]
tools/testing/selftests/bpf/progs/sendmsg4_prog.c [moved from tools/testing/selftests/bpf/sendmsg4_prog.c with 100% similarity]
tools/testing/selftests/bpf/progs/sendmsg6_prog.c [moved from tools/testing/selftests/bpf/sendmsg6_prog.c with 100% similarity]
tools/testing/selftests/bpf/progs/socket_cookie_prog.c [moved from tools/testing/selftests/bpf/socket_cookie_prog.c with 100% similarity]
tools/testing/selftests/bpf/progs/sockmap_parse_prog.c [moved from tools/testing/selftests/bpf/sockmap_parse_prog.c with 100% similarity]
tools/testing/selftests/bpf/progs/sockmap_tcp_msg_prog.c [moved from tools/testing/selftests/bpf/sockmap_tcp_msg_prog.c with 100% similarity]
tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c [moved from tools/testing/selftests/bpf/sockmap_verdict_prog.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_adjust_tail.c [moved from tools/testing/selftests/bpf/test_adjust_tail.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_btf_haskv.c [moved from tools/testing/selftests/bpf/test_btf_haskv.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_btf_nokv.c [moved from tools/testing/selftests/bpf/test_btf_nokv.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c [moved from tools/testing/selftests/bpf/test_get_stack_rawtp.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_l4lb.c [moved from tools/testing/selftests/bpf/test_l4lb.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_l4lb_noinline.c [moved from tools/testing/selftests/bpf/test_l4lb_noinline.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_lirc_mode2_kern.c [moved from tools/testing/selftests/bpf/test_lirc_mode2_kern.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_lwt_ip_encap.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_lwt_seg6local.c [moved from tools/testing/selftests/bpf/test_lwt_seg6local.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_map_in_map.c [moved from tools/testing/selftests/bpf/test_map_in_map.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_map_lock.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_obj_id.c [moved from tools/testing/selftests/bpf/test_obj_id.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_pkt_access.c [moved from tools/testing/selftests/bpf/test_pkt_access.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_pkt_md_access.c [moved from tools/testing/selftests/bpf/test_pkt_md_access.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_queue_map.c [moved from tools/testing/selftests/bpf/test_queue_map.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c [moved from tools/testing/selftests/bpf/test_select_reuseport_kern.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_sk_lookup_kern.c [moved from tools/testing/selftests/bpf/test_sk_lookup_kern.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_skb_cgroup_id_kern.c [moved from tools/testing/selftests/bpf/test_skb_cgroup_id_kern.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_sock_fields_kern.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_sockhash_kern.c [moved from tools/testing/selftests/bpf/test_sockhash_kern.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_sockmap_kern.c [moved from tools/testing/selftests/bpf/test_sockmap_kern.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_spin_lock.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_stack_map.c [moved from tools/testing/selftests/bpf/test_stack_map.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c [moved from tools/testing/selftests/bpf/test_stacktrace_build_id.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_stacktrace_map.c [moved from tools/testing/selftests/bpf/test_stacktrace_map.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_tcp_estats.c [moved from tools/testing/selftests/bpf/test_tcp_estats.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c [moved from tools/testing/selftests/bpf/test_tcpbpf_kern.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c [moved from tools/testing/selftests/bpf/test_tcpnotify_kern.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_tracepoint.c [moved from tools/testing/selftests/bpf/test_tracepoint.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_tunnel_kern.c [moved from tools/testing/selftests/bpf/test_tunnel_kern.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_xdp.c [moved from tools/testing/selftests/bpf/test_xdp.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_xdp_meta.c [moved from tools/testing/selftests/bpf/test_xdp_meta.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_xdp_noinline.c [moved from tools/testing/selftests/bpf/test_xdp_noinline.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_xdp_redirect.c [moved from tools/testing/selftests/bpf/test_xdp_redirect.c with 100% similarity]
tools/testing/selftests/bpf/progs/test_xdp_vlan.c [moved from tools/testing/selftests/bpf/test_xdp_vlan.c with 100% similarity]
tools/testing/selftests/bpf/progs/xdp_dummy.c [moved from tools/testing/selftests/bpf/xdp_dummy.c with 100% similarity]
tools/testing/selftests/bpf/tcp_client.py
tools/testing/selftests/bpf/tcp_server.py
tools/testing/selftests/bpf/test_btf.c
tools/testing/selftests/bpf/test_libbpf_open.c
tools/testing/selftests/bpf/test_lwt_ip_encap.sh [new file with mode: 0755]
tools/testing/selftests/bpf/test_maps.c
tools/testing/selftests/bpf/test_offload.py
tools/testing/selftests/bpf/test_progs.c
tools/testing/selftests/bpf/test_sock.c
tools/testing/selftests/bpf/test_sock_fields.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_verifier.c
tools/testing/selftests/bpf/verifier/ctx_sk_msg.c
tools/testing/selftests/bpf/verifier/ctx_skb.c
tools/testing/selftests/bpf/verifier/jmp32.c
tools/testing/selftests/bpf/verifier/jset.c
tools/testing/selftests/bpf/verifier/ref_tracking.c
tools/testing/selftests/bpf/verifier/sock.c [new file with mode: 0644]
tools/testing/selftests/bpf/verifier/spill_fill.c
tools/testing/selftests/bpf/verifier/spin_lock.c [new file with mode: 0644]
tools/testing/selftests/bpf/verifier/unpriv.c
tools/testing/selftests/bpf/verifier/value_ptr_arith.c
tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh
tools/testing/selftests/drivers/net/mlxsw/blackhole_routes.sh [new file with mode: 0755]
tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh
tools/testing/selftests/drivers/net/mlxsw/qos_dscp_router.sh
tools/testing/selftests/drivers/net/mlxsw/spectrum-2/tc_flower.sh
tools/testing/selftests/drivers/net/mlxsw/spectrum/resource_scale.sh
tools/testing/selftests/filesystems/binderfs/.gitignore [new file with mode: 0644]
tools/testing/selftests/filesystems/binderfs/Makefile [new file with mode: 0644]
tools/testing/selftests/filesystems/binderfs/binderfs_test.c [new file with mode: 0644]
tools/testing/selftests/filesystems/binderfs/config [new file with mode: 0644]
tools/testing/selftests/ir/Makefile
tools/testing/selftests/net/Makefile
tools/testing/selftests/net/forwarding/config
tools/testing/selftests/net/forwarding/forwarding.config.sample
tools/testing/selftests/net/forwarding/lib.sh
tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q_lag.sh
tools/testing/selftests/net/forwarding/mirror_gre_changes.sh
tools/testing/selftests/net/forwarding/mirror_gre_flower.sh
tools/testing/selftests/net/forwarding/mirror_gre_vlan_bridge_1q.sh
tools/testing/selftests/net/forwarding/mirror_lib.sh
tools/testing/selftests/net/forwarding/router_broadcast.sh
tools/testing/selftests/net/tls.c
tools/testing/selftests/net/xfrm_policy.sh
tools/testing/selftests/netfilter/Makefile
tools/testing/selftests/netfilter/config
tools/testing/selftests/netfilter/nft_nat.sh [new file with mode: 0755]
tools/testing/selftests/networking/timestamping/Makefile
tools/testing/selftests/networking/timestamping/rxtimestamp.c
tools/testing/selftests/proc/.gitignore
tools/testing/selftests/proc/Makefile
tools/testing/selftests/proc/setns-dcache.c [new file with mode: 0644]
tools/testing/selftests/seccomp/seccomp_bpf.c
tools/testing/selftests/timers/Makefile
tools/testing/selftests/x86/protection_keys.c
virt/kvm/kvm_main.c

index 9b64266..169fe08 100644 (file)
@@ -24,7 +24,7 @@ What:         /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/
                                                        cpld3_version
 
 Date:          November 2018
-KernelVersion: 4.21
+KernelVersion: 5.0
 Contact:       Vadim Pasternak <vadimpmellanox.com>
 Description:   These files show with which CPLD versions have been burned
                on LED board.
@@ -35,7 +35,7 @@ What:         /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/
                                                        jtag_enable
 
 Date:          November 2018
-KernelVersion: 4.21
+KernelVersion: 5.0
 Contact:       Vadim Pasternak <vadimpmellanox.com>
 Description:   These files enable and disable the access to the JTAG domain.
                By default access to the JTAG domain is disabled.
@@ -105,7 +105,7 @@ What:               /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/
                                                reset_voltmon_upgrade_fail
 
 Date:          November 2018
-KernelVersion: 4.21
+KernelVersion: 5.0
 Contact:       Vadim Pasternak <vadimpmellanox.com>
 Description:   These files show the system reset cause, as following: ComEx
                power fail, reset from ComEx, system platform reset, reset
index b799bcf..bcf2cd5 100644 (file)
                        possible to determine what the correct size should be.
                        This option provides an override for these situations.
 
+       carrier_timeout=
+                       [NET] Specifies amount of time (in seconds) that
+                       the kernel should wait for a network carrier. By default
+                       it waits 120 seconds.
+
        ca_keys=        [KEYS] This parameter identifies a specific key(s) on
                        the system trusted keyring to be used for certificate
                        trust validation.
                        By default, super page will be supported if Intel IOMMU
                        has the capability. With this option, super page will
                        not be supported.
-               sm_off [Default Off]
-                       By default, scalable mode will be supported if the
+               sm_on [Default Off]
+                       By default, scalable mode will be disabled even if the
                        hardware advertises that it has support for the scalable
                        mode translation. With this option set, scalable mode
-                       will not be used even on hardware which claims to support
-                       it.
+                       will be used on hardware which claims to support it.
                tboot_noforce [Default Off]
                        Do not force the Intel IOMMU enabled under tboot.
                        By default, tboot will force Intel IOMMU on, which
index 6e5cef0..50daa0b 100644 (file)
@@ -17,7 +17,11 @@ extra-y += $(DT_TMP_SCHEMA)
 quiet_cmd_mk_schema = SCHEMA  $@
       cmd_mk_schema = $(DT_MK_SCHEMA) $(DT_MK_SCHEMA_FLAGS) -o $@ $(filter-out FORCE, $^)
 
-DT_DOCS = $(shell cd $(srctree)/$(src) && find * -name '*.yaml')
+DT_DOCS = $(shell \
+       cd $(srctree)/$(src) && \
+       find * \( -name '*.yaml' ! -name $(DT_TMP_SCHEMA) \) \
+       )
+
 DT_SCHEMA_FILES ?= $(addprefix $(src)/,$(DT_DOCS))
 
 extra-y += $(patsubst $(src)/%.yaml,%.example.dts, $(DT_SCHEMA_FILES))
index aa3527f..47aa205 100644 (file)
@@ -3,12 +3,16 @@ Mediatek MT7530 Ethernet switch
 
 Required properties:
 
-- compatible: Must be compatible = "mediatek,mt7530";
+- compatible: may be compatible = "mediatek,mt7530"
+       or compatible = "mediatek,mt7621"
 - #address-cells: Must be 1.
 - #size-cells: Must be 0.
 - mediatek,mcm: Boolean; if defined, indicates that either MT7530 is the part
        on multi-chip module belong to MT7623A has or the remotely standalone
        chip as the function MT7623N reference board provided for.
+
+If compatible mediatek,mt7530 is set then the following properties are required
+
 - core-supply: Phandle to the regulator node necessary for the core power.
 - io-supply: Phandle to the regulator node necessary for the I/O power.
        See Documentation/devicetree/bindings/regulator/mt6323-regulator.txt
index 3e17ac1..174f292 100644 (file)
@@ -3,8 +3,8 @@
 Required properties:
 - compatible: Should be "cdns,[<chip>-]{macb|gem}"
   Use "cdns,at91rm9200-emac" Atmel at91rm9200 SoC.
-  Use "cdns,at91sam9260-macb" for Atmel at91sam9 SoCs or the 10/100Mbit IP
-  available on sama5d3 SoCs.
+  Use "cdns,at91sam9260-macb" for Atmel at91sam9 SoCs.
+  Use "cdns,sam9x60-macb" for Microchip sam9x60 SoC.
   Use "cdns,np4-macb" for NP4 SoC devices.
   Use "cdns,at32ap7000-macb" for other 10/100 usage or use the generic form: "cdns,macb".
   Use "cdns,pc302-gem" for Picochip picoXcell pc302 and later devices based on
index bedcfd5..691f886 100644 (file)
@@ -19,7 +19,7 @@ Optional properties:
   "marvell,armada-370-neta" and 9800B for others.
 - clock-names: List of names corresponding to clocks property; shall be
   "core" for core clock and "bus" for the optional bus clock.
-
+- phys: comphy for the ethernet port, see ../phy/phy-bindings.txt
 
 Optional properties (valid only for Armada XP/38x):
 
index e55af7f..85d7240 100644 (file)
@@ -1,17 +1,53 @@
 * NI XGE Ethernet controller
 
 Required properties:
-- compatible: Should be "ni,xge-enet-2.00"
-- reg: Address and length of the register set for the device
+- compatible: Should be "ni,xge-enet-3.00", but can be "ni,xge-enet-2.00" for
+              older device trees with DMA engines co-located in the address map,
+              with the one reg entry to describe the whole device.
+- reg: Address and length of the register set for the device. It contains the
+       information of registers in the same order as described by reg-names.
+- reg-names: Should contain the reg names
+       "dma":  DMA engine control and status region
+        "ctrl": MDIO and PHY control and status region
 - interrupts: Should contain tx and rx interrupt
 - interrupt-names: Should be "rx" and "tx"
 - phy-mode: See ethernet.txt file in the same directory.
-- phy-handle: See ethernet.txt file in the same directory.
 - nvmem-cells: Phandle of nvmem cell containing the MAC address
 - nvmem-cell-names: Should be "address"
 
+Optional properties:
+- mdio subnode to indicate presence of MDIO controller
+- fixed-link : Assume a fixed link. See fixed-link.txt in the same directory.
+  Use instead of phy-handle.
+- phy-handle: See ethernet.txt file in the same directory.
+
 Examples (10G generic PHY):
        nixge0: ethernet@40000000 {
+               compatible = "ni,xge-enet-3.00";
+               reg = <0x40000000 0x4000
+                      0x41002000 0x2000>;
+               reg-names = "dma", "ctrl";
+
+               nvmem-cells = <&eth1_addr>;
+               nvmem-cell-names = "address";
+
+               interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>, <0 30 IRQ_TYPE_LEVEL_HIGH>;
+               interrupt-names = "rx", "tx";
+               interrupt-parent = <&intc>;
+
+               phy-mode = "xgmii";
+               phy-handle = <&ethernet_phy1>;
+
+               mdio {
+                       ethernet_phy1: ethernet-phy@4 {
+                               compatible = "ethernet-phy-ieee802.3-c45";
+                               reg = <4>;
+                       };
+               };
+       };
+
+Examples (10G generic PHY, no MDIO):
+       nixge0: ethernet@40000000 {
                compatible = "ni,xge-enet-2.00";
                reg = <0x40000000 0x6000>;
 
@@ -24,9 +60,33 @@ Examples (10G generic PHY):
 
                phy-mode = "xgmii";
                phy-handle = <&ethernet_phy1>;
+       };
+
+Examples (1G generic fixed-link + MDIO):
+       nixge0: ethernet@40000000 {
+               compatible = "ni,xge-enet-2.00";
+               reg = <0x40000000 0x6000>;
 
-               ethernet_phy1: ethernet-phy@4 {
-                       compatible = "ethernet-phy-ieee802.3-c45";
-                       reg = <4>;
+               nvmem-cells = <&eth1_addr>;
+               nvmem-cell-names = "address";
+
+               interrupts = <0 29 IRQ_TYPE_LEVEL_HIGH>, <0 30 IRQ_TYPE_LEVEL_HIGH>;
+               interrupt-names = "rx", "tx";
+               interrupt-parent = <&intc>;
+
+               phy-mode = "xgmii";
+
+               fixed-link {
+                       speed = <1000>;
+                       pause;
+                       link-gpios = <&gpio0 63 GPIO_ACTIVE_HIGH>;
+               };
+
+               mdio {
+                       ethernet_phy1: ethernet-phy@4 {
+                               compatible = "ethernet-phy-ieee802.3-c22";
+                               reg = <4>;
+                       };
                };
+
        };
diff --git a/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt b/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt
new file mode 100644 (file)
index 0000000..ad49e5c
--- /dev/null
@@ -0,0 +1,40 @@
+mvebu armada 38x comphy driver
+------------------------------
+
+This comphy controller can be found on Marvell Armada 38x. It provides a
+number of shared PHYs used by various interfaces (network, sata, usb,
+PCIe...).
+
+Required properties:
+
+- compatible: should be "marvell,armada-380-comphy"
+- reg: should contain the comphy register location and length.
+- #address-cells: should be 1.
+- #size-cells: should be 0.
+
+A sub-node is required for each comphy lane provided by the comphy.
+
+Required properties (child nodes):
+
+- reg: comphy lane number.
+- #phy-cells : from the generic phy bindings, must be 1. Defines the
+               input port to use for a given comphy lane.
+
+Example:
+
+       comphy: phy@18300 {
+               compatible = "marvell,armada-380-comphy";
+               reg = <0x18300 0x100>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               cpm_comphy0: phy@0 {
+                       reg = <0>;
+                       #phy-cells = <1>;
+               };
+
+               cpm_comphy1: phy@1 {
+                       reg = <1>;
+                       #phy-cells = <1>;
+               };
+       };
index 8e7f855..454c937 100644 (file)
@@ -19,6 +19,9 @@ Clock Properties:
   - fsl,max-adj      Maximum frequency adjustment in parts per billion.
   - fsl,extts-fifo   The presence of this property indicates hardware
                     support for the external trigger stamp FIFO.
+  - little-endian    The presence of this property indicates the 1588 timer
+                    IP block is little-endian mode. The default endian mode
+                    is big-endian.
 
   These properties set the operational parameters for the PTP
   clock. You must choose these carefully for the clock to work right.
index 3660341..0e72183 100644 (file)
@@ -4,14 +4,10 @@ Required properties:
 - compatible : "olpc,ap-sp"
 - reg : base address and length of SoC's WTM registers
 - interrupts : SP-AP interrupt
-- clocks : phandle + clock-specifier for the clock that drives the WTM
-- clock-names:  should be "sp"
 
 Example:
        ap-sp@d4290000 {
                compatible = "olpc,ap-sp";
                reg = <0xd4290000 0x1000>;
                interrupts = <40>;
-               clocks = <&soc_clocks MMP2_CLK_SP>;
-               clock-names = "sp";
        }
index 85a8335..eab40bc 100644 (file)
@@ -126,6 +126,9 @@ functions/definitions
    :functions: ieee80211_rx_status
 
 .. kernel-doc:: include/net/mac80211.h
+   :functions: mac80211_rx_encoding_flags
+
+.. kernel-doc:: include/net/mac80211.h
    :functions: mac80211_rx_flags
 
 .. kernel-doc:: include/net/mac80211.h
index a188466..5045df9 100644 (file)
@@ -27,11 +27,12 @@ Driver Overview
 
 The DPIO driver is bound to DPIO objects discovered on the fsl-mc bus and
 provides services that:
-  A) allow other drivers, such as the Ethernet driver, to enqueue and dequeue
+
+  A. allow other drivers, such as the Ethernet driver, to enqueue and dequeue
      frames for their respective objects
-  B) allow drivers to register callbacks for data availability notifications
+  B. allow drivers to register callbacks for data availability notifications
      when data becomes available on a queue or channel
-  C) allow drivers to manage hardware buffer pools
+  C. allow drivers to manage hardware buffer pools
 
 The Linux DPIO driver consists of 3 primary components--
    DPIO object driver-- fsl-mc driver that manages the DPIO object
@@ -140,11 +141,10 @@ QBman portal interface (qbman-portal.c)
 
    The qbman-portal component provides APIs to do the low level hardware
    bit twiddling for operations such as:
-      -initializing Qman software portals
-
-      -building and sending portal commands
 
-      -portal interrupt configuration and processing
+      - initializing Qman software portals
+      - building and sending portal commands
+      - portal interrupt configuration and processing
 
    The qbman-portal APIs are not public to other drivers, and are
    only used by dpio-service.
index 5e2839b..2b9f488 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+==============================================================
 Linux* Base Driver for the Intel(R) PRO/100 Family of Adapters
 ==============================================================
 
index 6379d4d..956560b 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+===========================================================
 Linux* Base Driver for Intel(R) Ethernet Network Connection
 ===========================================================
 
index 33554e5..01999f0 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+======================================================
 Linux* Driver for Intel(R) Ethernet Network Connection
 ======================================================
 
index bf5e594..ac3269e 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+==============================================================
 Linux* Base Driver for Intel(R) Ethernet Multi-host Controller
 ==============================================================
 
index 0cc16c5..848fd38 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+==================================================================
 Linux* Base Driver for the Intel(R) Ethernet Controller 700 Series
 ==================================================================
 
index f8b42b6..2d0c3ba 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+==================================================================
 Linux* Base Driver for Intel(R) Ethernet Adaptive Virtual Function
 ==================================================================
 
index 4d118b8..c220aa2 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+===================================================================
 Linux* Base Driver for the Intel(R) Ethernet Connection E800 Series
 ===================================================================
 
index e87a4a7..fc8cfaa 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+===========================================================
 Linux* Base Driver for Intel(R) Ethernet Network Connection
 ===========================================================
 
index a8a9ffa..9cddabe 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+============================================================
 Linux* Base Virtual Function Driver for Intel(R) 1G Ethernet
 ============================================================
 
index 8bd80e2..9450182 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+=====================================================================
 Linux Base Driver for 10 Gigabit Intel(R) Ethernet Network Connection
 =====================================================================
 
index 86d887a..c7d2548 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+=============================================================================
 Linux* Base Driver for the Intel(R) Ethernet 10 Gigabit PCI Express Adapters
 =============================================================================
 
index 56cde63..5d49773 100644 (file)
@@ -1,5 +1,6 @@
 .. SPDX-License-Identifier: GPL-2.0+
 
+=============================================================
 Linux* Base Virtual Function Driver for Intel(R) 10G Ethernet
 =============================================================
 
index 2bb0707..1ae979f 100644 (file)
@@ -267,7 +267,7 @@ static struct fixed_phy_status stmmac0_fixed_phy_status = {
 
 During the board's device_init we can configure the first
 MAC for fixed_link by calling:
-  fixed_phy_add(PHY_POLL, 1, &stmmac0_fixed_phy_status, -1);
+  fixed_phy_add(PHY_POLL, 1, &stmmac0_fixed_phy_status);
 and the second one, with a real PHY device attached to the bus,
 by using the stmmac_mdio_bus_data structure (to provide the id, the
 reset procedure etc).
diff --git a/Documentation/networking/devlink-health.txt b/Documentation/networking/devlink-health.txt
new file mode 100644 (file)
index 0000000..1db3fbe
--- /dev/null
@@ -0,0 +1,86 @@
+The health mechanism is targeted for Real Time Alerting, in order to know when
+something bad had happened to a PCI device
+- Provide alert debug information
+- Self healing
+- If problem needs vendor support, provide a way to gather all needed debugging
+  information.
+
+The main idea is to unify and centralize driver health reports in the
+generic devlink instance and allow the user to set different
+attributes of the health reporting and recovery procedures.
+
+The devlink health reporter:
+Device driver creates a "health reporter" per each error/health type.
+Error/Health type can be a known/generic (eg pci error, fw error, rx/tx error)
+or unknown (driver specific).
+For each registered health reporter a driver can issue error/health reports
+asynchronously. All health reports handling is done by devlink.
+Device driver can provide specific callbacks for each "health reporter", e.g.
+ - Recovery procedures
+ - Diagnostics and object dump procedures
+ - OOB initial parameters
+Different parts of the driver can register different types of health reporters
+with different handlers.
+
+Once an error is reported, devlink health will do the following actions:
+  * A log is being send to the kernel trace events buffer
+  * Health status and statistics are being updated for the reporter instance
+  * Object dump is being taken and saved at the reporter instance (as long as
+    there is no other dump which is already stored)
+  * Auto recovery attempt is being done. Depends on:
+    - Auto-recovery configuration
+    - Grace period vs. time passed since last recover
+
+The user interface:
+User can access/change each reporter's parameters and driver specific callbacks
+via devlink, e.g per error type (per health reporter)
+ - Configure reporter's generic parameters (like: disable/enable auto recovery)
+ - Invoke recovery procedure
+ - Run diagnostics
+ - Object dump
+
+The devlink health interface (via netlink):
+DEVLINK_CMD_HEALTH_REPORTER_GET
+  Retrieves status and configuration info per DEV and reporter.
+DEVLINK_CMD_HEALTH_REPORTER_SET
+  Allows reporter-related configuration setting.
+DEVLINK_CMD_HEALTH_REPORTER_RECOVER
+  Triggers a reporter's recovery procedure.
+DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE
+  Retrieves diagnostics data from a reporter on a device.
+DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET
+  Retrieves the last stored dump. Devlink health
+  saves a single dump. If an dump is not already stored by the devlink
+  for this reporter, devlink generates a new dump.
+  dump output is defined by the reporter.
+DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR
+  Clears the last saved dump file for the specified reporter.
+
+
+                                               netlink
+                                      +--------------------------+
+                                      |                          |
+                                      |            +             |
+                                      |            |             |
+                                      +--------------------------+
+                                                   |request for ops
+                                                   |(diagnose,
+ mlx5_core                             devlink     |recover,
+                                                   |dump)
++--------+                            +--------------------------+
+|        |                            |    reporter|             |
+|        |                            |  +---------v----------+  |
+|        |   ops execution            |  |                    |  |
+|     <----------------------------------+                    |  |
+|        |                            |  |                    |  |
+|        |                            |  + ^------------------+  |
+|        |                            |    | request for ops     |
+|        |                            |    | (recover, dump)     |
+|        |                            |    |                     |
+|        |                            |  +-+------------------+  |
+|        |     health report          |  | health handler     |  |
+|        +------------------------------->                    |  |
+|        |                            |  +--------------------+  |
+|        |     health reporter create |                          |
+|        +---------------------------->                          |
++--------+                            +--------------------------+
diff --git a/Documentation/networking/devlink-info-versions.rst b/Documentation/networking/devlink-info-versions.rst
new file mode 100644 (file)
index 0000000..c79ad85
--- /dev/null
@@ -0,0 +1,43 @@
+.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+
+=====================
+Devlink info versions
+=====================
+
+board.id
+========
+
+Unique identifier of the board design.
+
+board.rev
+=========
+
+Board design revision.
+
+board.manufacture
+=================
+
+An identifier of the company or the facility which produced the part.
+
+fw.mgmt
+=======
+
+Control unit firmware version. This firmware is responsible for house
+keeping tasks, PHY control etc. but not the packet-by-packet data path
+operation.
+
+fw.app
+======
+
+Data path microcode controlling high-speed packet processing.
+
+fw.undi
+=======
+
+UNDI software, may include the UEFI driver, firmware or both.
+
+fw.ncsi
+=======
+
+Version of the software responsible for supporting/handling the
+Network Controller Sideband Interface.
index 2c5c67a..c63ea9f 100644 (file)
@@ -1,2 +1,10 @@
 fw_load_policy         [DEVICE, GENERIC]
                        Configuration mode: driverinit
+
+acl_region_rehash_interval     [DEVICE, DRIVER-SPECIFIC]
+                       Sets an interval for periodic ACL region rehashes.
+                       The value is in milliseconds, minimal value is "3000".
+                       Value "0" disables the periodic work.
+                       The first rehash will be run right after value is set.
+                       Type: u32
+                       Configuration mode: runtime
index 01603bc..b5e060e 100644 (file)
@@ -464,10 +464,11 @@ breakpoints: 0 1
 JIT compiler
 ------------
 
-The Linux kernel has a built-in BPF JIT compiler for x86_64, SPARC, PowerPC,
-ARM, ARM64, MIPS and s390 and can be enabled through CONFIG_BPF_JIT. The JIT
-compiler is transparently invoked for each attached filter from user space
-or for internal kernel users if it has been previously enabled by root:
+The Linux kernel has a built-in BPF JIT compiler for x86_64, SPARC,
+PowerPC, ARM, ARM64, MIPS, RISC-V and s390 and can be enabled through
+CONFIG_BPF_JIT. The JIT compiler is transparently invoked for each
+attached filter from user space or for internal kernel users if it has
+been previously enabled by root:
 
   echo 1 > /proc/sys/net/core/bpf_jit_enable
 
@@ -603,9 +604,10 @@ got from bpf_prog_create(), and 'ctx' the given context (e.g.
 skb pointer). All constraints and restrictions from bpf_check_classic() apply
 before a conversion to the new layout is being done behind the scenes!
 
-Currently, the classic BPF format is being used for JITing on most 32-bit
-architectures, whereas x86-64, aarch64, s390x, powerpc64, sparc64, arm32 perform
-JIT compilation from eBPF instruction set.
+Currently, the classic BPF format is being used for JITing on most
+32-bit architectures, whereas x86-64, aarch64, s390x, powerpc64,
+sparc64, arm32, riscv (RV64G) perform JIT compilation from eBPF
+instruction set.
 
 Some core changes of the new internal format:
 
index f1627ca..9a32451 100644 (file)
@@ -24,6 +24,7 @@ Contents:
    device_drivers/intel/i40e
    device_drivers/intel/iavf
    device_drivers/intel/ice
+   devlink-info-versions
    kapi
    z8530book
    msg_zerocopy
index 355c6d8..b203d13 100644 (file)
@@ -22,8 +22,9 @@ and changeable from userspace under certain rules.
 2. Querying from userspace
 
 Both admin and operational state can be queried via the netlink
-operation RTM_GETLINK. It is also possible to subscribe to RTMGRP_LINK
-to be notified of updates. This is important for setting from userspace.
+operation RTM_GETLINK. It is also possible to subscribe to RTNLGRP_LINK
+to be notified of updates while the interface is admin up. This is
+important for setting from userspace.
 
 These values contain interface state:
 
@@ -101,8 +102,9 @@ because some driver controlled protocol establishment has to
 complete. Corresponding functions are netif_dormant_on() to set the
 flag, netif_dormant_off() to clear it and netif_dormant() to query.
 
-On device allocation, networking core sets the flags equivalent to
-netif_carrier_ok() and !netif_dormant().
+On device allocation, both flags __LINK_STATE_NOCARRIER and
+__LINK_STATE_DORMANT are cleared, so the effective state is equivalent
+to netif_carrier_ok() and !netif_dormant().
 
 
 Whenever the driver CHANGES one of these flags, a workqueue event is
@@ -133,11 +135,11 @@ netif_carrier_ok() && !netif_dormant() is set by the
 driver. Afterwards, the userspace application can set IFLA_OPERSTATE
 to IF_OPER_DORMANT or IF_OPER_UP as long as the driver does not set
 netif_carrier_off() or netif_dormant_on(). Changes made by userspace
-are multicasted on the netlink group RTMGRP_LINK.
+are multicasted on the netlink group RTNLGRP_LINK.
 
 So basically a 802.1X supplicant interacts with the kernel like this:
 
--subscribe to RTMGRP_LINK
+-subscribe to RTNLGRP_LINK
 -set IFLA_LINKMODE to 1 via RTM_SETLINK
 -query RTM_GETLINK once to get initial state
 -if initial flags are not (IFF_LOWER_UP && !IFF_DORMANT), wait until
index c5642f4..52b026b 100644 (file)
@@ -367,16 +367,19 @@ to the accept queue.
 TCP Fast Open
 =============
 * TcpEstabResets
+
 Defined in `RFC1213 tcpEstabResets`_.
 
 .. _RFC1213 tcpEstabResets: https://tools.ietf.org/html/rfc1213#page-48
 
 * TcpAttemptFails
+
 Defined in `RFC1213 tcpAttemptFails`_.
 
 .. _RFC1213 tcpAttemptFails: https://tools.ietf.org/html/rfc1213#page-48
 
 * TcpOutRsts
+
 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
@@ -384,6 +387,30 @@ process might be failed due to some errors (e.g. memory alloc failed).
 
 .. _RFC1213 tcpOutRsts: https://tools.ietf.org/html/rfc1213#page-52
 
+* TcpExtTCPSpuriousRtxHostQueues
+
+When the TCP stack wants to retransmit a packet, and finds that packet
+is not lost in the network, but the packet is not sent yet, the TCP
+stack would give up the retransmission and update this counter. It
+might happen if a packet stays too long time in a qdisc or driver
+queue.
+
+* TcpEstabResets
+
+The socket receives a RST packet in Establish or CloseWait state.
+
+* TcpExtTCPKeepAlive
+
+This counter indicates many keepalive packets were sent. The keepalive
+won't be enabled by default. A userspace program could enable it by
+setting the SO_KEEPALIVE socket option.
+
+* TcpExtTCPSpuriousRTOs
+
+The spurious retransmission timeout detected by the `F-RTO`_
+algorithm.
+
+.. _F-RTO: https://tools.ietf.org/html/rfc5682
 
 TCP Fast Path
 ============
@@ -609,6 +636,29 @@ packet yet, the sender would know packet 4 is out of order. The TCP
 stack of kernel will increase TcpExtTCPSACKReorder for both of the
 above scenarios.
 
+* TcpExtTCPSlowStartRetrans
+
+The TCP stack wants to retransmit a packet and the congestion control
+state is 'Loss'.
+
+* TcpExtTCPFastRetrans
+
+The TCP stack wants to retransmit a packet and the congestion control
+state is not 'Loss'.
+
+* TcpExtTCPLostRetransmit
+
+A SACK points out that a retransmission packet is lost again.
+
+* TcpExtTCPRetransFail
+
+The TCP stack tries to deliver a retransmission packet to lower layers
+but the lower layers return an error.
+
+* TcpExtTCPSynRetrans
+
+The TCP stack retransmits a SYN packet.
+
 DSACK
 =====
 The DSACK is defined in `RFC2883`_. The receiver uses DSACK to report
@@ -790,8 +840,9 @@ unacknowledged number (more strict than `RFC 5961 section 5.2`_).
 .. _RFC 5961 section 5.2: https://tools.ietf.org/html/rfc5961#page-11
 
 TCP receive window
-=================
+==================
 * TcpExtTCPWantZeroWindowAdv
+
 Depending on current memory usage, the TCP stack tries to set receive
 window to zero. But the receive window might still be a no-zero
 value. For example, if the previous window size is 10, and the TCP
@@ -799,14 +850,16 @@ stack receives 3 bytes, the current window size would be 7 even if the
 window size calculated by the memory usage is zero.
 
 * TcpExtTCPToZeroWindowAdv
+
 The TCP receive window is set to zero from a no-zero value.
 
 * TcpExtTCPFromZeroWindowAdv
+
 The TCP receive window is set to no-zero value from zero.
 
 
 Delayed ACK
-==========
+===========
 The TCP Delayed ACK is a technique which is used for reducing the
 packet count in the network. For more details, please refer the
 `Delayed ACK wiki`_
@@ -814,10 +867,12 @@ packet count in the network. For more details, please refer the
 .. _Delayed ACK wiki: https://en.wikipedia.org/wiki/TCP_delayed_acknowledgment
 
 * TcpExtDelayedACKs
+
 A delayed ACK timer expires. The TCP stack will send a pure ACK packet
 and exit the delayed ACK mode.
 
 * TcpExtDelayedACKLocked
+
 A delayed ACK timer expires, but the TCP stack can't send an ACK
 immediately due to the socket is locked by a userspace program. The
 TCP stack will send a pure ACK later (after the userspace program
@@ -826,24 +881,147 @@ TCP stack will also update TcpExtDelayedACKs and exit the delayed ACK
 mode.
 
 * TcpExtDelayedACKLost
+
 It will be updated when the TCP stack receives a packet which has been
 ACKed. A Delayed ACK loss might cause this issue, but it would also be
 triggered by other reasons, such as a packet is duplicated in the
 network.
 
 Tail Loss Probe (TLP)
-===================
+=====================
 TLP is an algorithm which is used to detect TCP packet loss. For more
 details, please refer the `TLP paper`_.
 
 .. _TLP paper: https://tools.ietf.org/html/draft-dukkipati-tcpm-tcp-loss-probe-01
 
 * TcpExtTCPLossProbes
+
 A TLP probe packet is sent.
 
 * TcpExtTCPLossProbeRecovery
+
 A packet loss is detected and recovered by TLP.
 
+TCP Fast Open
+=============
+TCP Fast Open is a technology which allows data transfer before the
+3-way handshake complete. Please refer the `TCP Fast Open wiki`_ for a
+general description.
+
+.. _TCP Fast Open wiki: https://en.wikipedia.org/wiki/TCP_Fast_Open
+
+* TcpExtTCPFastOpenActive
+
+When the TCP stack receives an ACK packet in the SYN-SENT status, and
+the ACK packet acknowledges the data in the SYN packet, the TCP stack
+understand the TFO cookie is accepted by the other side, then it
+updates this counter.
+
+* TcpExtTCPFastOpenActiveFail
+
+This counter indicates that the TCP stack initiated a TCP Fast Open,
+but it failed. This counter would be updated in three scenarios: (1)
+the other side doesn't acknowledge the data in the SYN packet. (2) The
+SYN packet which has the TFO cookie is timeout at least once. (3)
+after the 3-way handshake, the retransmission timeout happens
+net.ipv4.tcp_retries1 times, because some middle-boxes may black-hole
+fast open after the handshake.
+
+* TcpExtTCPFastOpenPassive
+
+This counter indicates how many times the TCP stack accepts the fast
+open request.
+
+* TcpExtTCPFastOpenPassiveFail
+
+This counter indicates how many times the TCP stack rejects the fast
+open request. It is caused by either the TFO cookie is invalid or the
+TCP stack finds an error during the socket creating process.
+
+* TcpExtTCPFastOpenListenOverflow
+
+When the pending fast open request number is larger than
+fastopenq->max_qlen, the TCP stack will reject the fast open request
+and update this counter. When this counter is updated, the TCP stack
+won't update TcpExtTCPFastOpenPassive or
+TcpExtTCPFastOpenPassiveFail. The fastopenq->max_qlen is set by the
+TCP_FASTOPEN socket operation and it could not be larger than
+net.core.somaxconn. For example:
+
+setsockopt(sfd, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen));
+
+* TcpExtTCPFastOpenCookieReqd
+
+This counter indicates how many times a client wants to request a TFO
+cookie.
+
+SYN cookies
+===========
+SYN cookies are used to mitigate SYN flood, for details, please refer
+the `SYN cookies wiki`_.
+
+.. _SYN cookies wiki: https://en.wikipedia.org/wiki/SYN_cookies
+
+* TcpExtSyncookiesSent
+
+It indicates how many SYN cookies are sent.
+
+* TcpExtSyncookiesRecv
+
+How many reply packets of the SYN cookies the TCP stack receives.
+
+* TcpExtSyncookiesFailed
+
+The MSS decoded from the SYN cookie is invalid. When this counter is
+updated, the received packet won't be treated as a SYN cookie and the
+TcpExtSyncookiesRecv counter wont be updated.
+
+Challenge ACK
+=============
+For details of challenge ACK, please refer the explaination of
+TcpExtTCPACKSkippedChallenge.
+
+* TcpExtTCPChallengeACK
+
+The number of challenge acks sent.
+
+* TcpExtTCPSYNChallenge
+
+The number of challenge acks sent in response to SYN packets. After
+updates this counter, the TCP stack might send a challenge ACK and
+update the TcpExtTCPChallengeACK counter, or it might also skip to
+send the challenge and update the TcpExtTCPACKSkippedChallenge.
+
+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,
+copy the contiguous skbs to the single big skb, and free these
+contiguous skbs.
+
+* TcpExtPruneCalled
+
+The TCP stack tries to reclaim memory for a socket. After updates this
+counter, the TCP stack will try to collapse the out of order queue and
+the receiving queue. If the memory is still not enough, the TCP stack
+will try to discard packets from the out of order queue (and update the
+TcpExtOfoPruned counter)
+
+* TcpExtOfoPruned
+
+The TCP stack tries to discard packet on the out of order queue.
+
+* TcpExtRcvPruned
+
+After 'collapse' and discard packets from the out of order queue, if
+the actually used memory is still larger than the max allowed memory,
+this counter will be updated. It means the 'prune' fails.
+
+* TcpExtTCPRcvCollapsed
+
+This counter indicates how many skbs are freed during 'collapse'.
+
 examples
 ========
 
index 9d1432e..bbdaf89 100644 (file)
@@ -6,11 +6,21 @@ The interfaces for receiving network packages timestamps are:
 * SO_TIMESTAMP
   Generates a timestamp for each incoming packet in (not necessarily
   monotonic) system time. Reports the timestamp via recvmsg() in a
-  control message as struct timeval (usec resolution).
+  control message in usec resolution.
+  SO_TIMESTAMP is defined as SO_TIMESTAMP_NEW or SO_TIMESTAMP_OLD
+  based on the architecture type and time_t representation of libc.
+  Control message format is in struct __kernel_old_timeval for
+  SO_TIMESTAMP_OLD and in struct __kernel_sock_timeval for
+  SO_TIMESTAMP_NEW options respectively.
 
 * SO_TIMESTAMPNS
   Same timestamping mechanism as SO_TIMESTAMP, but reports the
-  timestamp as struct timespec (nsec resolution).
+  timestamp as struct timespec in nsec resolution.
+  SO_TIMESTAMPNS is defined as SO_TIMESTAMPNS_NEW or SO_TIMESTAMPNS_OLD
+  based on the architecture type and time_t representation of libc.
+  Control message format is in struct timespec for SO_TIMESTAMPNS_OLD
+  and in struct __kernel_timespec for SO_TIMESTAMPNS_NEW options
+  respectively.
 
 * IP_MULTICAST_LOOP + SO_TIMESTAMP[NS]
   Only for multicast:approximate transmit timestamp obtained by
@@ -22,7 +32,7 @@ The interfaces for receiving network packages timestamps are:
   timestamps for stream sockets.
 
 
-1.1 SO_TIMESTAMP:
+1.1 SO_TIMESTAMP (also SO_TIMESTAMP_OLD and SO_TIMESTAMP_NEW):
 
 This socket option enables timestamping of datagrams on the reception
 path. Because the destination socket, if any, is not known early in
@@ -31,15 +41,25 @@ same is true for all early receive timestamp options.
 
 For interface details, see `man 7 socket`.
 
+Always use SO_TIMESTAMP_NEW timestamp to always get timestamp in
+struct __kernel_sock_timeval format.
 
-1.2 SO_TIMESTAMPNS:
+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):
 
 This option is identical to SO_TIMESTAMP except for the returned data type.
 Its struct timespec allows for higher resolution (ns) timestamps than the
 timeval of SO_TIMESTAMP (ms).
 
+Always use SO_TIMESTAMPNS_NEW timestamp to always get timestamp in
+struct __kernel_timespec format.
+
+SO_TIMESTAMPNS_OLD returns incorrect timestamps after the year 2038
+on 32 bit machines.
 
-1.3 SO_TIMESTAMPING:
+1.3 SO_TIMESTAMPING (also SO_TIMESTAMPING_OLD and SO_TIMESTAMPING_NEW):
 
 Supports multiple types of timestamp requests. As a result, this
 socket option takes a bitmap of flags, not a boolean. In
@@ -323,10 +343,23 @@ SO_TIMESTAMP and SO_TIMESTAMPNS records can be retrieved.
 These timestamps are returned in a control message with cmsg_level
 SOL_SOCKET, cmsg_type SCM_TIMESTAMPING, and payload of type
 
+For SO_TIMESTAMPING_OLD:
+
 struct scm_timestamping {
        struct timespec ts[3];
 };
 
+For SO_TIMESTAMPING_NEW:
+
+struct scm_timestamping64 {
+       struct __kernel_timespec ts[3];
+
+Always use SO_TIMESTAMPING_NEW timestamp to always get timestamp in
+struct scm_timestamping64 format.
+
+SO_TIMESTAMPING_OLD returns incorrect timestamps after the year 2038
+on 32 bit machines.
+
 The structure can return up to three timestamps. This is a legacy
 feature. At least one field is non-zero at any time. Most timestamps
 are passed in ts[0]. Hardware timestamps are passed in ts[2].
index 819caf8..ebc679b 100644 (file)
@@ -56,26 +56,34 @@ of any kernel data structures.
 
 dentry-state:
 
-From linux/fs/dentry.c:
+From linux/include/linux/dcache.h:
 --------------------------------------------------------------
-struct {
+struct dentry_stat_t dentry_stat {
         int nr_dentry;
         int nr_unused;
         int age_limit;         /* age in seconds */
         int want_pages;        /* pages requested by system */
-        int dummy[2];
-} dentry_stat = {0, 0, 45, 0,};
--------------------------------------------------------------- 
-
-Dentries are dynamically allocated and deallocated, and
-nr_dentry seems to be 0 all the time. Hence it's safe to
-assume that only nr_unused, age_limit and want_pages are
-used. Nr_unused seems to be exactly what its name says.
+        int nr_negative;       /* # of unused negative dentries */
+        int dummy;             /* Reserved for future use */
+};
+--------------------------------------------------------------
+
+Dentries are dynamically allocated and deallocated.
+
+nr_dentry shows the total number of dentries allocated (active
++ unused). nr_unused shows the number of dentries that are not
+actively used, but are saved in the LRU list for future reuse.
+
 Age_limit is the age in seconds after which dcache entries
 can be reclaimed when memory is short and want_pages is
 nonzero when shrink_dcache_pages() has been called and the
 dcache isn't pruned yet.
 
+nr_negative shows the number of unused dentries that are also
+negative dentries which do not map to any files. Instead,
+they help speeding up rejection of non-existing files provided
+by the users.
+
 ==============================================================
 
 dquot-max & dquot-nr:
index bc06807..2ae91d3 100644 (file)
@@ -52,6 +52,7 @@ two flavors of JITs, the newer eBPF JIT currently supported on:
   - sparc64
   - mips64
   - s390x
+  - riscv
 
 And the older cBPF JIT supported on the following archs:
   - mips
index e8e8d14..c1f95b5 100644 (file)
@@ -9,7 +9,7 @@ Fenghua Yu <fenghua.yu@intel.com>
 Tony Luck <tony.luck@intel.com>
 Vikas Shivappa <vikas.shivappa@intel.com>
 
-This feature is enabled by the CONFIG_X86_RESCTRL and the x86 /proc/cpuinfo
+This feature is enabled by the CONFIG_X86_CPU_RESCTRL and the x86 /proc/cpuinfo
 flag bits:
 RDT (Resource Director Technology) Allocation - "rdt_a"
 CAT (Cache Allocation Technology) - "cat_l3", "cat_l2"
index ecbc22e..8c8f509 100644 (file)
@@ -2848,6 +2848,9 @@ F:        include/uapi/linux/if_bonding.h
 BPF (Safe dynamic programs and tools)
 M:     Alexei Starovoitov <ast@kernel.org>
 M:     Daniel Borkmann <daniel@iogearbox.net>
+R:     Martin KaFai Lau <kafai@fb.com>
+R:     Song Liu <songliubraving@fb.com>
+R:     Yonghong Song <yhs@fb.com>
 L:     netdev@vger.kernel.org
 L:     linux-kernel@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git
@@ -2873,6 +2876,8 @@ F:        samples/bpf/
 F:     tools/bpf/
 F:     tools/lib/bpf/
 F:     tools/testing/selftests/bpf/
+K:     bpf
+N:     bpf
 
 BPF JIT for ARM
 M:     Shubham Bansal <illusionist.neo@gmail.com>
@@ -2907,6 +2912,12 @@ L:       netdev@vger.kernel.org
 S:     Maintained
 F:     arch/powerpc/net/
 
+BPF JIT for RISC-V (RV64G)
+M:     Björn Töpel <bjorn.topel@gmail.com>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     arch/riscv/net/
+
 BPF JIT for S390
 M:     Martin Schwidefsky <schwidefsky@de.ibm.com>
 M:     Heiko Carstens <heiko.carstens@de.ibm.com>
@@ -4123,7 +4134,7 @@ S:        Maintained
 F:     drivers/media/dvb-frontends/cxd2820r*
 
 CXGB3 ETHERNET DRIVER (CXGB3)
-M:     Arjun Vynipadath <arjun@chelsio.com>
+M:     Vishal Kulkarni <vishal@chelsio.com>
 L:     netdev@vger.kernel.org
 W:     http://www.chelsio.com
 S:     Supported
@@ -4152,7 +4163,7 @@ S:        Supported
 F:     drivers/crypto/chelsio
 
 CXGB4 ETHERNET DRIVER (CXGB4)
-M:     Arjun Vynipadath <arjun@chelsio.com>
+M:     Vishal Kulkarni <vishal@chelsio.com>
 L:     netdev@vger.kernel.org
 W:     http://www.chelsio.com
 S:     Supported
@@ -5181,7 +5192,7 @@ DRM DRIVERS FOR XEN
 M:     Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
 T:     git git://anongit.freedesktop.org/drm/drm-misc
 L:     dri-devel@lists.freedesktop.org
-L:     xen-devel@lists.xen.org
+L:     xen-devel@lists.xenproject.org (moderated for non-subscribers)
 S:     Supported
 F:     drivers/gpu/drm/xen/
 F:     Documentation/gpu/xen-front.rst
@@ -6093,6 +6104,7 @@ FREESCALE QORIQ PTP CLOCK DRIVER
 M:     Yangbo Lu <yangbo.lu@nxp.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
+F:     drivers/net/ethernet/freescale/enetc/enetc_ptp.c
 F:     drivers/ptp/ptp_qoriq.c
 F:     drivers/ptp/ptp_qoriq_debugfs.c
 F:     include/linux/fsl/ptp_qoriq.h
@@ -6153,7 +6165,7 @@ FREESCALE SOC SOUND DRIVERS
 M:     Timur Tabi <timur@kernel.org>
 M:     Nicolin Chen <nicoleotsuka@gmail.com>
 M:     Xiubo Li <Xiubo.Lee@gmail.com>
-R:     Fabio Estevam <fabio.estevam@nxp.com>
+R:     Fabio Estevam <festevam@gmail.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 L:     linuxppc-dev@lists.ozlabs.org
 S:     Maintained
@@ -10901,7 +10913,7 @@ F:      include/linux/nvmem-consumer.h
 F:     include/linux/nvmem-provider.h
 
 NXP SGTL5000 DRIVER
-M:     Fabio Estevam <fabio.estevam@nxp.com>
+M:     Fabio Estevam <festevam@gmail.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 S:     Maintained
 F:     Documentation/devicetree/bindings/sound/sgtl5000.txt
@@ -11315,10 +11327,12 @@ F:    include/dt-bindings/
 
 OPENCORES I2C BUS DRIVER
 M:     Peter Korsgaard <peter@korsgaard.com>
+M:     Andrew Lunn <andrew@lunn.ch>
 L:     linux-i2c@vger.kernel.org
 S:     Maintained
 F:     Documentation/i2c/busses/i2c-ocores
 F:     drivers/i2c/busses/i2c-ocores.c
+F:     include/linux/platform_data/i2c-ocores.h
 
 OPENRISC ARCHITECTURE
 M:     Jonas Bonn <jonas@southpole.se>
@@ -12884,6 +12898,13 @@ F:     Documentation/devicetree/bindings/net/dsa/realtek-smi.txt
 F:     drivers/net/dsa/realtek-smi*
 F:     drivers/net/dsa/rtl83*
 
+REDPINE WIRELESS DRIVER
+M:     Amitkumar Karwar <amitkarwar@gmail.com>
+M:     Siva Rebbagondla <siva8118@gmail.com>
+L:     linux-wireless@vger.kernel.org
+S:     Maintained
+F:     drivers/net/wireless/rsi/
+
 REGISTER MAP ABSTRACTION
 M:     Mark Brown <broonie@kernel.org>
 L:     linux-kernel@vger.kernel.org
@@ -13712,6 +13733,15 @@ L:     netdev@vger.kernel.org
 S:     Supported
 F:     drivers/net/ethernet/sfc/
 
+SFF/SFP/SFP+ MODULE SUPPORT
+M:     Russell King <linux@armlinux.org.uk>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     drivers/net/phy/phylink.c
+F:     drivers/net/phy/sfp*
+F:     include/linux/phylink.h
+F:     include/linux/sfp.h
+
 SGI GRU DRIVER
 M:     Dimitri Sivanich <sivanich@sgi.com>
 S:     Maintained
@@ -13733,6 +13763,7 @@ F:      drivers/misc/sgi-xp/
 
 SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
 M:     Ursula Braun <ubraun@linux.ibm.com>
+M:     Karsten Graul <kgraul@linux.ibm.com>
 L:     linux-s390@vger.kernel.org
 W:     http://www.ibm.com/developerworks/linux/linux390/
 S:     Supported
@@ -16657,6 +16688,15 @@ S:     Maintained
 F:     drivers/platform/x86/
 F:     drivers/platform/olpc/
 
+X86 PLATFORM DRIVERS - ARCH
+R:     Darren Hart <dvhart@infradead.org>
+R:     Andy Shevchenko <andy@infradead.org>
+L:     platform-driver-x86@vger.kernel.org
+L:     x86@kernel.org
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/core
+S:     Maintained
+F:     arch/x86/platform
+
 X86 VDSO
 M:     Andy Lutomirski <luto@kernel.org>
 L:     linux-kernel@vger.kernel.org
@@ -16689,6 +16729,24 @@ T:     git git://linuxtv.org/media_tree.git
 S:     Maintained
 F:     drivers/media/tuners/tuner-xc2028.*
 
+XDP (eXpress Data Path)
+M:     Alexei Starovoitov <ast@kernel.org>
+M:     Daniel Borkmann <daniel@iogearbox.net>
+M:     David S. Miller <davem@davemloft.net>
+M:     Jakub Kicinski <jakub.kicinski@netronome.com>
+M:     Jesper Dangaard Brouer <hawk@kernel.org>
+M:     John Fastabend <john.fastabend@gmail.com>
+L:     netdev@vger.kernel.org
+L:     xdp-newbies@vger.kernel.org
+S:     Supported
+F:     net/core/xdp.c
+F:     include/net/xdp.h
+F:     kernel/bpf/devmap.c
+F:     kernel/bpf/cpumap.c
+F:     include/trace/events/xdp.h
+K:     xdp
+N:     xdp
+
 XDP SOCKETS (AF_XDP)
 M:     Björn Töpel <bjorn.topel@intel.com>
 M:     Magnus Karlsson <magnus.karlsson@intel.com>
index f5b1d0d..86cf35d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 VERSION = 5
 PATCHLEVEL = 0
 SUBLEVEL = 0
-EXTRAVERSION = -rc3
+EXTRAVERSION = -rc6
 NAME = Shy Crocodile
 
 # *DOCUMENTATION*
index 4d17cac..432402c 100644 (file)
 
 #elif defined(CONFIG_ALPHA_DP264) || \
       defined(CONFIG_ALPHA_LYNX)  || \
-      defined(CONFIG_ALPHA_SHARK) || \
-      defined(CONFIG_ALPHA_EIGER)
+      defined(CONFIG_ALPHA_SHARK)
 # define NR_IRQS       64
 
 #elif defined(CONFIG_ALPHA_TITAN)
 #define NR_IRQS                80
 
 #elif defined(CONFIG_ALPHA_RAWHIDE) || \
-       defined(CONFIG_ALPHA_TAKARA)
+      defined(CONFIG_ALPHA_TAKARA) || \
+      defined(CONFIG_ALPHA_EIGER)
 # define NR_IRQS       128
 
 #elif defined(CONFIG_ALPHA_WILDFIRE)
index b1c9b54..0d0fddb 100644 (file)
@@ -3,6 +3,7 @@
 #define _UAPI_ASM_SOCKET_H
 
 #include <asm/sockios.h>
+#include <asm/bitsperlong.h>
 
 /* For setsockopt(2) */
 /*
@@ -30,8 +31,8 @@
 #define SO_RCVBUFFORCE 0x100b
 #define        SO_RCVLOWAT     0x1010
 #define        SO_SNDLOWAT     0x1011
-#define        SO_RCVTIMEO     0x1012
-#define        SO_SNDTIMEO     0x1013
+#define        SO_RCVTIMEO_OLD 0x1012
+#define        SO_SNDTIMEO_OLD 0x1013
 #define SO_ACCEPTCONN  0x1014
 #define SO_PROTOCOL    0x1028
 #define SO_DOMAIN      0x1029
 #define SO_GET_FILTER          SO_ATTACH_FILTER
 
 #define SO_PEERNAME            28
-#define SO_TIMESTAMP           29
-#define SCM_TIMESTAMP          SO_TIMESTAMP
 
 #define SO_PEERSEC             30
 #define SO_PASSSEC             34
-#define SO_TIMESTAMPNS         35
-#define SCM_TIMESTAMPNS                SO_TIMESTAMPNS
 
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             19
@@ -66,9 +63,6 @@
 
 #define SO_MARK                        36
 
-#define SO_TIMESTAMPING                37
-#define SCM_TIMESTAMPING       SO_TIMESTAMPING
-
 #define SO_RXQ_OVFL             40
 
 #define SO_WIFI_STATUS         41
 
 #define SO_BINDTOIFINDEX       62
 
+#define SO_TIMESTAMP_OLD        29
+#define SO_TIMESTAMPNS_OLD      35
+#define SO_TIMESTAMPING_OLD     37
+
+#define SO_TIMESTAMP_NEW        63
+#define SO_TIMESTAMPNS_NEW      64
+#define SO_TIMESTAMPING_NEW     65
+
+#define SO_RCVTIMEO_NEW         66
+#define SO_SNDTIMEO_NEW         67
+
+#if !defined(__KERNEL__)
+
+#if __BITS_PER_LONG == 64
+#define SO_TIMESTAMP           SO_TIMESTAMP_OLD
+#define SO_TIMESTAMPNS         SO_TIMESTAMPNS_OLD
+#define SO_TIMESTAMPING         SO_TIMESTAMPING_OLD
+
+#define SO_RCVTIMEO            SO_RCVTIMEO_OLD
+#define SO_SNDTIMEO            SO_SNDTIMEO_OLD
+#else
+#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
+#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW)
+#define SO_TIMESTAMPING (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPING_OLD : SO_TIMESTAMPING_NEW)
+
+#define SO_RCVTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_RCVTIMEO_OLD : SO_RCVTIMEO_NEW)
+#define SO_SNDTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_SNDTIMEO_OLD : SO_SNDTIMEO_NEW)
+#endif
+
+#define SCM_TIMESTAMP           SO_TIMESTAMP
+#define SCM_TIMESTAMPNS         SO_TIMESTAMPNS
+#define SCM_TIMESTAMPING        SO_TIMESTAMPING
+
+#endif
+
 #endif /* _UAPI_ASM_SOCKET_H */
index d73dc47..188fc92 100644 (file)
@@ -78,7 +78,7 @@ __load_new_mm_context(struct mm_struct *next_mm)
 /* Macro for exception fixup code to access integer registers.  */
 #define dpf_reg(r)                                                     \
        (((unsigned long *)regs)[(r) <= 8 ? (r) : (r) <= 15 ? (r)-16 :  \
-                                (r) <= 18 ? (r)+8 : (r)-10])
+                                (r) <= 18 ? (r)+10 : (r)-10])
 
 asmlinkage void
 do_page_fault(unsigned long address, unsigned long mmcsr,
index d0fd688..5b25006 100644 (file)
        pinctrl-names = "default";
        pinctrl-0 = <&mmc1_pins>;
        bus-width = <0x4>;
-       cd-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
+       cd-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
        cd-inverted;
        max-frequency = <26000000>;
        vmmc-supply = <&vmmcsd_fixed>;
index 1b0d068..0d81600 100644 (file)
@@ -93,6 +93,7 @@
        bm,pool-long = <2>;
        bm,pool-short = <1>;
        buffer-manager = <&bm>;
+       phys = <&comphy1 1>;
        phy-mode = "sgmii";
        status = "okay";
 };
        bm,pool-short = <1>;
        buffer-manager = <&bm>;
        managed = "in-band-status";
+       phys = <&comphy5 2>;
        phy-mode = "sgmii";
        sfp = <&sfp>;
        status = "okay";
index 929459c..7b2e2bd 100644 (file)
                                #clock-cells = <1>;
                        };
 
+                       comphy: phy@18300 {
+                               compatible = "marvell,armada-380-comphy";
+                               reg = <0x18300 0x100>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               comphy0: phy@0 {
+                                       reg = <0>;
+                                       #phy-cells = <1>;
+                               };
+
+                               comphy1: phy@1 {
+                                       reg = <1>;
+                                       #phy-cells = <1>;
+                               };
+
+                               comphy2: phy@2 {
+                                       reg = <2>;
+                                       #phy-cells = <1>;
+                               };
+
+                               comphy3: phy@3 {
+                                       reg = <3>;
+                                       #phy-cells = <1>;
+                               };
+
+                               comphy4: phy@4 {
+                                       reg = <4>;
+                                       #phy-cells = <1>;
+                               };
+
+                               comphy5: phy@5 {
+                                       reg = <5>;
+                                       #phy-cells = <1>;
+                               };
+                       };
+
                        coreclk: mvebu-sar@18600 {
                                compatible = "marvell,armada-380-core-clock";
                                reg = <0x18600 0x04>;
index 47aa53b..559659b 100644 (file)
                clocksource: timer@20000 {
                        compatible = "ti,da830-timer";
                        reg = <0x20000 0x1000>;
-                       interrupts = <12>, <13>;
+                       interrupts = <21>, <22>;
                        interrupt-names = "tint12", "tint34";
                        clocks = <&pll0_auxclk>;
                };
index 5edf858..a31b17e 100644 (file)
                power {
                        label = "Power Button";
                        gpios = <&gpio2 12 GPIO_ACTIVE_LOW>;
-                       gpio-key,wakeup;
+                       wakeup-source;
                        linux,code = <KEY_POWER>;
                };
        };
index d816370..4a31a41 100644 (file)
        pinctrl-2 = <&pinctrl_usdhc3_200mhz>;
        cd-gpios = <&gpio3 22 GPIO_ACTIVE_LOW>;
        keep-power-in-suspend;
-       enable-sdio-wakeup;
+       wakeup-source;
        vmmc-supply = <&reg_sd3_vmmc>;
        status = "okay";
 };
index 272ff61..d1375d3 100644 (file)
                        };
 
                        gpt: gpt@2098000 {
-                               compatible = "fsl,imx6sx-gpt", "fsl,imx31-gpt";
+                               compatible = "fsl,imx6sx-gpt", "fsl,imx6dl-gpt";
                                reg = <0x02098000 0x4000>;
                                interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
                                clocks = <&clks IMX6SX_CLK_GPT_BUS>,
index e4645f6..2ab7486 100644 (file)
                        compatible = "amlogic,meson6-dwmac", "snps,dwmac";
                        reg = <0xc9410000 0x10000
                               0xc1108108 0x4>;
-                       interrupts = <GIC_SPI 8 IRQ_TYPE_EDGE_RISING>;
+                       interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
                        interrupt-names = "macirq";
                        status = "disabled";
                };
index 0872f6e..d50fc2f 100644 (file)
                cap-sd-highspeed;
                disable-wp;
 
-               cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-               cd-inverted;
+               cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
                vmmc-supply = <&vcc_3v3>;
        };
index 58669ab..0f0a46d 100644 (file)
                /* Realtek RTL8211F (0x001cc916) */
                eth_phy: ethernet-phy@0 {
                        reg = <0>;
-                       eee-broken-1000t;
                        interrupt-parent = <&gpio_intc>;
                        /* GPIOH_3 */
                        interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
                cap-sd-highspeed;
                disable-wp;
 
-               cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-               cd-inverted;
+               cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
                vmmc-supply = <&tflash_vdd>;
                vqmmc-supply = <&tf_io>;
index f585361..6ac02be 100644 (file)
                cap-sd-highspeed;
                disable-wp;
 
-               cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-               cd-inverted;
+               cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
                vmmc-supply = <&vcc_3v3>;
        };
index ddc7a7b..f57acf8 100644 (file)
                        interrupts-extended = <
                                &cpcap 15 0 &cpcap 14 0 &cpcap 28 0 &cpcap 19 0
                                &cpcap 18 0 &cpcap 17 0 &cpcap 16 0 &cpcap 49 0
-                               &cpcap 48 1
+                               &cpcap 48 0
                        >;
                        interrupt-names =
                                "id_ground", "id_float", "se0conn", "vbusvld",
index e53d326..93b4209 100644 (file)
 
        vdda-supply = <&vdac>;
 
-       #address-cells = <1>;
-       #size-cells = <0>;
-
        port {
-               reg = <0>;
                venc_out: endpoint {
                        remote-endpoint = <&opa_in>;
                        ti,channels = <1>;
index 182a539..826920e 100644 (file)
        /* For debugging, it is often good idea to remove this GPIO.
           It means you can remove back cover (to reboot by removing
           battery) and still use the MMC card. */
-       cd-gpios = <&gpio6 0 GPIO_ACTIVE_HIGH>; /* 160 */
+       cd-gpios = <&gpio6 0 GPIO_ACTIVE_LOW>; /* 160 */
 };
 
 /* most boards use vaux3, only some old versions use vmmc2 instead */
index 0d9b853..e142e6c 100644 (file)
                compatible = "ti,omap2-onenand";
                reg = <0 0 0x20000>;    /* CS0, offset 0, IO size 128K */
 
+               /*
+                * These timings are based on CONFIG_OMAP_GPMC_DEBUG=y reported
+                * bootloader set values when booted with v4.19 using both N950
+                * and N9 devices (OneNAND Manufacturer: Samsung):
+                *
+                *   gpmc cs0 before gpmc_cs_program_settings:
+                *   cs0 GPMC_CS_CONFIG1: 0xfd001202
+                *   cs0 GPMC_CS_CONFIG2: 0x00181800
+                *   cs0 GPMC_CS_CONFIG3: 0x00030300
+                *   cs0 GPMC_CS_CONFIG4: 0x18001804
+                *   cs0 GPMC_CS_CONFIG5: 0x03171d1d
+                *   cs0 GPMC_CS_CONFIG6: 0x97080000
+                */
                gpmc,sync-read;
                gpmc,sync-write;
                gpmc,burst-length = <16>;
                gpmc,device-width = <2>;
                gpmc,mux-add-data = <2>;
                gpmc,cs-on-ns = <0>;
-               gpmc,cs-rd-off-ns = <87>;
-               gpmc,cs-wr-off-ns = <87>;
+               gpmc,cs-rd-off-ns = <122>;
+               gpmc,cs-wr-off-ns = <122>;
                gpmc,adv-on-ns = <0>;
-               gpmc,adv-rd-off-ns = <10>;
-               gpmc,adv-wr-off-ns = <10>;
-               gpmc,oe-on-ns = <15>;
-               gpmc,oe-off-ns = <87>;
+               gpmc,adv-rd-off-ns = <15>;
+               gpmc,adv-wr-off-ns = <15>;
+               gpmc,oe-on-ns = <20>;
+               gpmc,oe-off-ns = <122>;
                gpmc,we-on-ns = <0>;
-               gpmc,we-off-ns = <87>;
-               gpmc,rd-cycle-ns = <112>;
-               gpmc,wr-cycle-ns = <112>;
-               gpmc,access-ns = <81>;
+               gpmc,we-off-ns = <122>;
+               gpmc,rd-cycle-ns = <148>;
+               gpmc,wr-cycle-ns = <148>;
+               gpmc,access-ns = <117>;
                gpmc,page-burst-access-ns = <15>;
                gpmc,bus-turnaround-ns = <0>;
                gpmc,cycle2cycle-delay-ns = <0>;
                gpmc,wait-monitoring-ns = <0>;
-               gpmc,clk-activation-ns = <5>;
-               gpmc,wr-data-mux-bus-ns = <30>;
-               gpmc,wr-access-ns = <81>;
-               gpmc,sync-clk-ps = <15000>;
+               gpmc,clk-activation-ns = <10>;
+               gpmc,wr-data-mux-bus-ns = <40>;
+               gpmc,wr-access-ns = <117>;
+
+               gpmc,sync-clk-ps = <15000>; /* TBC; Where this value came? */
 
                /*
                 * MTD partition table corresponding to Nokia's MeeGo 1.2
index 9c7e309..0960348 100644 (file)
                                        <SYSC_IDLE_SMART>,
                                        <SYSC_IDLE_SMART_WKUP>;
                        ti,syss-mask = <1>;
-                       ti,no-reset-on-init;
-                       ti,no-idle-on-init;
                        /* Domains (V, P, C): core, core_pwrdm, l4per_clkdm */
                        clocks = <&l4per_clkctrl OMAP5_UART3_CLKCTRL 0>;
                        clock-names = "fck";
index 3cc33f7..3adc158 100644 (file)
 
                du: display@feb00000 {
                        compatible = "renesas,du-r8a7743";
-                       reg = <0 0xfeb00000 0 0x40000>,
-                             <0 0xfeb90000 0 0x1c>;
-                       reg-names = "du", "lvds.0";
+                       reg = <0 0xfeb00000 0 0x40000>;
                        interrupts = <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>,
                                     <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>;
                        clocks = <&cpg CPG_MOD 724>,
-                                <&cpg CPG_MOD 723>,
-                                <&cpg CPG_MOD 726>;
-                       clock-names = "du.0", "du.1", "lvds.0";
+                                <&cpg CPG_MOD 723>;
+                       clock-names = "du.0", "du.1";
                        status = "disabled";
 
                        ports {
                                port@1 {
                                        reg = <1>;
                                        du_out_lvds0: endpoint {
+                                               remote-endpoint = <&lvds0_in>;
+                                       };
+                               };
+                       };
+               };
+
+               lvds0: lvds@feb90000 {
+                       compatible = "renesas,r8a7743-lvds";
+                       reg = <0 0xfeb90000 0 0x1c>;
+                       clocks = <&cpg CPG_MOD 726>;
+                       power-domains = <&sysc R8A7743_PD_ALWAYS_ON>;
+                       resets = <&cpg 726>;
+                       status = "disabled";
+
+                       ports {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               port@0 {
+                                       reg = <0>;
+                                       lvds0_in: endpoint {
+                                               remote-endpoint = <&du_out_lvds0>;
+                                       };
+                               };
+                               port@1 {
+                                       reg = <1>;
+                                       lvds0_out: endpoint {
                                        };
                                };
                        };
index 353d90f..13304b8 100644 (file)
                        #clock-cells = <0>;
                        compatible = "fixed-clock";
                        clock-frequency = <24000000>;
+                       clock-output-names = "osc24M";
                };
 
                osc32k: clk-32k {
index 5d23667..25540b7 100644 (file)
@@ -53,7 +53,7 @@
 
        aliases {
                serial0 = &uart0;
-               /* ethernet0 is the H3 emac, defined in sun8i-h3.dtsi */
+               ethernet0 = &emac;
                ethernet1 = &sdiowifi;
        };
 
index 689c893..b08d561 100644 (file)
        bus-num = <3>;
        status = "okay";
        spi-slave;
+       #address-cells = <0>;
 
-       slave@0 {
+       slave {
                compatible = "lwn,bk4";
                spi-max-frequency = <30000000>;
-               reg = <0>;
        };
 };
 
index 318394e..95a11d5 100644 (file)
@@ -83,7 +83,7 @@ static void __iomem *cns3xxx_pci_map_bus(struct pci_bus *bus,
        } else /* remote PCI bus */
                base = cnspci->cfg1_regs + ((busno & 0xf) << 20);
 
-       return base + (where & 0xffc) + (devfn << 12);
+       return base + where + (devfn << 12);
 }
 
 static int cns3xxx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
@@ -93,7 +93,7 @@ static int cns3xxx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
        u32 mask = (0x1ull << (size * 8)) - 1;
        int shift = (where % 4) * 8;
 
-       ret = pci_generic_config_read32(bus, devfn, where, size, val);
+       ret = pci_generic_config_read(bus, devfn, where, size, val);
 
        if (ret == PCIBIOS_SUCCESSFUL && !bus->number && !devfn &&
            (where & 0xffc) == PCI_CLASS_REVISION)
index 3b73813..23e8c93 100644 (file)
@@ -75,8 +75,7 @@ void __init n2100_map_io(void)
 /*
  * N2100 PCI.
  */
-static int __init
-n2100_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+static int n2100_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
        int irq;
 
index 028e50c..a32c3b6 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/suspend.h>
 #include <asm/suspend.h>
 #include "smc.h"
+#include "pm.h"
 
 static int tango_pm_powerdown(unsigned long arg)
 {
@@ -24,10 +25,7 @@ static const struct platform_suspend_ops tango_pm_ops = {
        .valid = suspend_valid_only_mem,
 };
 
-static int __init tango_pm_init(void)
+void __init tango_pm_init(void)
 {
        suspend_set_ops(&tango_pm_ops);
-       return 0;
 }
-
-late_initcall(tango_pm_init);
diff --git a/arch/arm/mach-tango/pm.h b/arch/arm/mach-tango/pm.h
new file mode 100644 (file)
index 0000000..35ea705
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifdef CONFIG_SUSPEND
+void __init tango_pm_init(void);
+#else
+#define tango_pm_init NULL
+#endif
index 677dd7b..824f907 100644 (file)
@@ -2,6 +2,7 @@
 #include <asm/mach/arch.h>
 #include <asm/hardware/cache-l2x0.h>
 #include "smc.h"
+#include "pm.h"
 
 static void tango_l2c_write(unsigned long val, unsigned int reg)
 {
@@ -15,4 +16,5 @@ DT_MACHINE_START(TANGO_DT, "Sigma Tango DT")
        .dt_compat      = tango_dt_compat,
        .l2c_aux_mask   = ~0,
        .l2c_write_sec  = tango_l2c_write,
+       .init_late      = tango_pm_init,
 MACHINE_END
index ed36dca..f519199 100644 (file)
@@ -190,8 +190,6 @@ static int pxa_ssp_remove(struct platform_device *pdev)
        if (ssp == NULL)
                return -ENODEV;
 
-       iounmap(ssp->mmio_base);
-
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        release_mem_region(res->start, resource_size(res));
 
@@ -201,7 +199,6 @@ static int pxa_ssp_remove(struct platform_device *pdev)
        list_del(&ssp->node);
        mutex_unlock(&ssp_lock);
 
-       kfree(ssp);
        return 0;
 }
 
index cb44aa2..e1d44b9 100644 (file)
@@ -7,7 +7,6 @@
 #include <linux/of_address.h>
 #include <linux/slab.h>
 #include <linux/types.h>
-#include <linux/dma-mapping.h>
 #include <linux/vmalloc.h>
 #include <linux/swiotlb.h>
 
index b0c64f7..8974b5a 100644 (file)
                reg = <0x3a3>;
                interrupt-parent = <&r_intc>;
                interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+               x-powers,drive-vbus-en; /* set N_VBUSEN as output pin */
        };
 };
 
index 837a03d..2abb335 100644 (file)
                };
 
                video-codec@1c0e000 {
-                       compatible = "allwinner,sun50i-h5-video-engine";
+                       compatible = "allwinner,sun50i-a64-video-engine";
                        reg = <0x01c0e000 0x1000>;
                        clocks = <&ccu CLK_BUS_VE>, <&ccu CLK_VE>,
                                 <&ccu CLK_DRAM_VE>;
index e14e0ce..016641a 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vddao_3v3>;
        vqmmc-supply = <&vddio_boot>;
index 8cd50b7..ade2ee0 100644 (file)
        max-frequency = <200000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vddio_ao3v3>;
        vqmmc-supply = <&vddio_tf>;
index 4cf7f6e..25105ac 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vddao_3v3>;
        vqmmc-supply = <&vddio_card>;
index 2e1cd5e..1cc9dc6 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&tflash_vdd>;
        vqmmc-supply = <&tf_io>;
index ce86226..0be0f2a 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vddao_3v3>;
        vqmmc-supply = <&vddio_card>;
index 93a4acf..ad4d50b 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vcc_3v3>;
 };
index ec09bb5..2d2db78 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vddao_3v3>;
        vqmmc-supply = <&vcc_3v3>;
index f1c410e..796baea 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vddao_3v3>;
        vqmmc-supply = <&vddio_card>;
index db29344..255cede 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vcc_3v3>;
        vqmmc-supply = <&vcc_card>;
index 6739697..9cbdb85 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vddao_3v3>;
        vqmmc-supply = <&vddio_card>;
index a1b3101..bc811a2 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vddao_3v3>;
        vqmmc-supply = <&vddio_boot>;
index 3c3a667..3f086ed 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vddao_3v3>;
        vqmmc-supply = <&vddio_boot>;
index f7a1cff..8acfd40 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vddao_3v3>;
        vqmmc-supply = <&vddio_boot>;
index 7212dc4..7fa20a8 100644 (file)
        max-frequency = <100000000>;
        disable-wp;
 
-       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
-       cd-inverted;
+       cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_LOW>;
 
        vmmc-supply = <&vddao_3v3>;
        vqmmc-supply = <&vddio_boot>;
index 99b7495..838e32c 100644 (file)
                };
 
                intc: interrupt-controller@9bc0000 {
-                       compatible = "arm,gic-v3";
+                       compatible = "qcom,msm8996-gic-v3", "arm,gic-v3";
                        #interrupt-cells = <3>;
                        interrupt-controller;
                        #redistributor-regions = <1>;
index 20745a8..719ed9d 100644 (file)
                                 <&cpg CPG_CORE R8A774A1_CLK_S3D1>,
                                 <&scif_clk>;
                        clock-names = "fck", "brg_int", "scif_clk";
+                       dmas = <&dmac1 0x13>, <&dmac1 0x12>,
+                              <&dmac2 0x13>, <&dmac2 0x12>;
+                       dma-names = "tx", "rx", "tx", "rx";
                        power-domains = <&sysc R8A774A1_PD_ALWAYS_ON>;
                        resets = <&cpg 310>;
                        status = "disabled";
index afedbf5..0648d12 100644 (file)
                                 <&cpg CPG_CORE R8A7796_CLK_S3D1>,
                                 <&scif_clk>;
                        clock-names = "fck", "brg_int", "scif_clk";
+                       dmas = <&dmac1 0x13>, <&dmac1 0x12>,
+                              <&dmac2 0x13>, <&dmac2 0x12>;
+                       dma-names = "tx", "rx", "tx", "rx";
                        power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
                        resets = <&cpg 310>;
                        status = "disabled";
index 6dc9b1f..4b3730f 100644 (file)
                                 <&cpg CPG_CORE R8A77965_CLK_S3D1>,
                                 <&scif_clk>;
                        clock-names = "fck", "brg_int", "scif_clk";
+                       dmas = <&dmac1 0x13>, <&dmac1 0x12>,
+                              <&dmac2 0x13>, <&dmac2 0x12>;
+                       dma-names = "tx", "rx", "tx", "rx";
                        power-domains = <&sysc R8A77965_PD_ALWAYS_ON>;
                        resets = <&cpg 310>;
                        status = "disabled";
index 29cdc99..9859e11 100644 (file)
@@ -299,8 +299,10 @@ int swsusp_arch_suspend(void)
                dcache_clean_range(__idmap_text_start, __idmap_text_end);
 
                /* Clean kvm setup code to PoC? */
-               if (el2_reset_needed())
+               if (el2_reset_needed()) {
                        dcache_clean_range(__hyp_idmap_text_start, __hyp_idmap_text_end);
+                       dcache_clean_range(__hyp_text_start, __hyp_text_end);
+               }
 
                /* make the crash dump kernel image protected again */
                crash_post_resume();
index e1261fb..17f325b 100644 (file)
@@ -28,6 +28,8 @@
 #include <asm/virt.h>
 
        .text
+       .pushsection    .hyp.text, "ax"
+
        .align 11
 
 ENTRY(__hyp_stub_vectors)
index ba6b417..b09b6f7 100644 (file)
@@ -88,6 +88,7 @@ u64 __init kaslr_early_init(u64 dt_phys)
         * we end up running with module randomization disabled.
         */
        module_alloc_base = (u64)_etext - MODULES_VSIZE;
+       __flush_dcache_area(&module_alloc_base, sizeof(module_alloc_base));
 
        /*
         * Try to map the FDT early. If this fails, we simply bail,
index f2c211a..5887133 100644 (file)
@@ -120,10 +120,12 @@ static int create_dtb(struct kimage *image,
 {
        void *buf;
        size_t buf_size;
+       size_t cmdline_len;
        int ret;
 
+       cmdline_len = cmdline ? strlen(cmdline) : 0;
        buf_size = fdt_totalsize(initial_boot_params)
-                       + strlen(cmdline) + DTB_EXTRA_SPACE;
+                       + cmdline_len + DTB_EXTRA_SPACE;
 
        for (;;) {
                buf = vmalloc(buf_size);
index 2a5b338..f17afb9 100644 (file)
@@ -478,13 +478,13 @@ bool arch_within_kprobe_blacklist(unsigned long addr)
            addr < (unsigned long)__entry_text_end) ||
            (addr >= (unsigned long)__idmap_text_start &&
            addr < (unsigned long)__idmap_text_end) ||
+           (addr >= (unsigned long)__hyp_text_start &&
+           addr < (unsigned long)__hyp_text_end) ||
            !!search_exception_tables(addr))
                return true;
 
        if (!is_kernel_in_hyp_mode()) {
-               if ((addr >= (unsigned long)__hyp_text_start &&
-                   addr < (unsigned long)__hyp_text_end) ||
-                   (addr >= (unsigned long)__hyp_idmap_text_start &&
+               if ((addr >= (unsigned long)__hyp_idmap_text_start &&
                    addr < (unsigned long)__hyp_idmap_text_end))
                        return true;
        }
index fcb1f2a..99bb8fa 100644 (file)
@@ -286,74 +286,73 @@ static void note_page(struct pg_state *st, unsigned long addr, unsigned level,
 
 }
 
-static void walk_pte(struct pg_state *st, pmd_t *pmdp, unsigned long start)
+static void walk_pte(struct pg_state *st, pmd_t *pmdp, unsigned long start,
+                    unsigned long end)
 {
-       pte_t *ptep = pte_offset_kernel(pmdp, 0UL);
-       unsigned long addr;
-       unsigned i;
+       unsigned long addr = start;
+       pte_t *ptep = pte_offset_kernel(pmdp, start);
 
-       for (i = 0; i < PTRS_PER_PTE; i++, ptep++) {
-               addr = start + i * PAGE_SIZE;
+       do {
                note_page(st, addr, 4, READ_ONCE(pte_val(*ptep)));
-       }
+       } while (ptep++, addr += PAGE_SIZE, addr != end);
 }
 
-static void walk_pmd(struct pg_state *st, pud_t *pudp, unsigned long start)
+static void walk_pmd(struct pg_state *st, pud_t *pudp, unsigned long start,
+                    unsigned long end)
 {
-       pmd_t *pmdp = pmd_offset(pudp, 0UL);
-       unsigned long addr;
-       unsigned i;
+       unsigned long next, addr = start;
+       pmd_t *pmdp = pmd_offset(pudp, start);
 
-       for (i = 0; i < PTRS_PER_PMD; i++, pmdp++) {
+       do {
                pmd_t pmd = READ_ONCE(*pmdp);
+               next = pmd_addr_end(addr, end);
 
-               addr = start + i * PMD_SIZE;
                if (pmd_none(pmd) || pmd_sect(pmd)) {
                        note_page(st, addr, 3, pmd_val(pmd));
                } else {
                        BUG_ON(pmd_bad(pmd));
-                       walk_pte(st, pmdp, addr);
+                       walk_pte(st, pmdp, addr, next);
                }
-       }
+       } while (pmdp++, addr = next, addr != end);
 }
 
-static void walk_pud(struct pg_state *st, pgd_t *pgdp, unsigned long start)
+static void walk_pud(struct pg_state *st, pgd_t *pgdp, unsigned long start,
+                    unsigned long end)
 {
-       pud_t *pudp = pud_offset(pgdp, 0UL);
-       unsigned long addr;
-       unsigned i;
+       unsigned long next, addr = start;
+       pud_t *pudp = pud_offset(pgdp, start);
 
-       for (i = 0; i < PTRS_PER_PUD; i++, pudp++) {
+       do {
                pud_t pud = READ_ONCE(*pudp);
+               next = pud_addr_end(addr, end);
 
-               addr = start + i * PUD_SIZE;
                if (pud_none(pud) || pud_sect(pud)) {
                        note_page(st, addr, 2, pud_val(pud));
                } else {
                        BUG_ON(pud_bad(pud));
-                       walk_pmd(st, pudp, addr);
+                       walk_pmd(st, pudp, addr, next);
                }
-       }
+       } while (pudp++, addr = next, addr != end);
 }
 
 static void walk_pgd(struct pg_state *st, struct mm_struct *mm,
                     unsigned long start)
 {
-       pgd_t *pgdp = pgd_offset(mm, 0UL);
-       unsigned i;
-       unsigned long addr;
+       unsigned long end = (start < TASK_SIZE_64) ? TASK_SIZE_64 : 0;
+       unsigned long next, addr = start;
+       pgd_t *pgdp = pgd_offset(mm, start);
 
-       for (i = 0; i < PTRS_PER_PGD; i++, pgdp++) {
+       do {
                pgd_t pgd = READ_ONCE(*pgdp);
+               next = pgd_addr_end(addr, end);
 
-               addr = start + i * PGDIR_SIZE;
                if (pgd_none(pgd)) {
                        note_page(st, addr, 1, pgd_val(pgd));
                } else {
                        BUG_ON(pgd_bad(pgd));
-                       walk_pud(st, pgdp, addr);
+                       walk_pud(st, pgdp, addr, next);
                }
-       }
+       } while (pgdp++, addr = next, addr != end);
 }
 
 void ptdump_walk_pgd(struct seq_file *m, struct ptdump_info *info)
index 30695a8..5c9073b 100644 (file)
@@ -33,7 +33,11 @@ void sync_icache_aliases(void *kaddr, unsigned long len)
                __clean_dcache_area_pou(kaddr, len);
                __flush_icache_all();
        } else {
-               flush_icache_range(addr, addr + len);
+               /*
+                * Don't issue kick_all_cpus_sync() after I-cache invalidation
+                * for user mappings.
+                */
+               __flush_icache_range(addr, addr + len);
        }
 }
 
index 33a2c94..63b4a17 100644 (file)
@@ -30,6 +30,7 @@ generic-y += pgalloc.h
 generic-y += preempt.h
 generic-y += segment.h
 generic-y += serial.h
+generic-y += shmparam.h
 generic-y += tlbflush.h
 generic-y += topology.h
 generic-y += trace_clock.h
index 6c6f630..0febf1a 100644 (file)
@@ -1,5 +1,4 @@
 include include/uapi/asm-generic/Kbuild.asm
 
 generic-y += kvm_para.h
-generic-y += shmparam.h
 generic-y += ucontext.h
index cd400d3..961c1dc 100644 (file)
@@ -40,6 +40,7 @@ generic-y += preempt.h
 generic-y += scatterlist.h
 generic-y += sections.h
 generic-y += serial.h
+generic-y += shmparam.h
 generic-y += sizes.h
 generic-y += spinlock.h
 generic-y += timex.h
index 6c6f630..0febf1a 100644 (file)
@@ -1,5 +1,4 @@
 include include/uapi/asm-generic/Kbuild.asm
 
 generic-y += kvm_para.h
-generic-y += shmparam.h
 generic-y += ucontext.h
index 47c4da3..b25fd42 100644 (file)
@@ -30,6 +30,7 @@ generic-y += rwsem.h
 generic-y += sections.h
 generic-y += segment.h
 generic-y += serial.h
+generic-y += shmparam.h
 generic-y += sizes.h
 generic-y += topology.h
 generic-y += trace_clock.h
index 61d955c..c1b06dc 100644 (file)
@@ -1,4 +1,3 @@
 include include/uapi/asm-generic/Kbuild.asm
 
-generic-y += shmparam.h
 generic-y += ucontext.h
index 5b819e5..b71c5f7 100644 (file)
@@ -2,3 +2,4 @@ include include/uapi/asm-generic/Kbuild.asm
 
 generated-y += unistd_64.h
 generic-y += kvm_para.h
+generic-y += socket.h
diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h
deleted file mode 100644 (file)
index ba0d245..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _ASM_IA64_SOCKET_H
-#define _ASM_IA64_SOCKET_H
-
-/*
- * Socket related defines.
- *
- * Based on <asm-i386/socket.h>.
- *
- * Modified 1998-2000
- *     David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co
- */
-
-#include <asm/sockios.h>
-
-/* For setsockopt(2) */
-#define SOL_SOCKET     1
-
-#define SO_DEBUG       1
-#define SO_REUSEADDR   2
-#define SO_TYPE                3
-#define SO_ERROR       4
-#define SO_DONTROUTE   5
-#define SO_BROADCAST   6
-#define SO_SNDBUF      7
-#define SO_RCVBUF      8
-#define SO_SNDBUFFORCE 32
-#define SO_RCVBUFFORCE 33
-#define SO_KEEPALIVE   9
-#define SO_OOBINLINE   10
-#define SO_NO_CHECK    11
-#define SO_PRIORITY    12
-#define SO_LINGER      13
-#define SO_BSDCOMPAT   14
-#define SO_REUSEPORT   15
-#define SO_PASSCRED    16
-#define SO_PEERCRED    17
-#define SO_RCVLOWAT    18
-#define SO_SNDLOWAT    19
-#define SO_RCVTIMEO    20
-#define SO_SNDTIMEO    21
-
-/* Security levels - as per NRL IPv6 - don't actually do anything */
-#define SO_SECURITY_AUTHENTICATION             22
-#define SO_SECURITY_ENCRYPTION_TRANSPORT       23
-#define SO_SECURITY_ENCRYPTION_NETWORK         24
-
-#define SO_BINDTODEVICE                25
-
-/* Socket filtering */
-#define SO_ATTACH_FILTER       26
-#define SO_DETACH_FILTER       27
-#define SO_GET_FILTER          SO_ATTACH_FILTER
-
-#define SO_PEERNAME            28
-#define SO_TIMESTAMP           29
-#define SCM_TIMESTAMP          SO_TIMESTAMP
-
-#define SO_ACCEPTCONN          30
-
-#define SO_PEERSEC             31
-#define SO_PASSSEC             34
-#define SO_TIMESTAMPNS         35
-#define SCM_TIMESTAMPNS                SO_TIMESTAMPNS
-
-#define SO_MARK                        36
-
-#define SO_TIMESTAMPING                37
-#define SCM_TIMESTAMPING       SO_TIMESTAMPING
-
-#define SO_PROTOCOL            38
-#define SO_DOMAIN              39
-
-#define SO_RXQ_OVFL             40
-
-#define SO_WIFI_STATUS         41
-#define SCM_WIFI_STATUS                SO_WIFI_STATUS
-#define SO_PEEK_OFF            42
-
-/* Instruct lower device to use last 4-bytes of skb data as FCS */
-#define SO_NOFCS               43
-
-#define SO_LOCK_FILTER         44
-
-#define SO_SELECT_ERR_QUEUE    45
-
-#define SO_BUSY_POLL           46
-
-#define SO_MAX_PACING_RATE     47
-
-#define SO_BPF_EXTENSIONS      48
-
-#define SO_INCOMING_CPU                49
-
-#define SO_ATTACH_BPF          50
-#define SO_DETACH_BPF          SO_DETACH_FILTER
-
-#define SO_ATTACH_REUSEPORT_CBPF       51
-#define SO_ATTACH_REUSEPORT_EBPF       52
-
-#define SO_CNX_ADVICE          53
-
-#define SCM_TIMESTAMPING_OPT_STATS     54
-
-#define SO_MEMINFO             55
-
-#define SO_INCOMING_NAPI_ID    56
-
-#define SO_COOKIE              57
-
-#define SCM_TIMESTAMPING_PKTINFO       58
-
-#define SO_PEERGROUPS          59
-
-#define SO_ZEROCOPY            60
-
-#define SO_TXTIME              61
-#define SCM_TXTIME             SO_TXTIME
-
-#define SO_BINDTOIFINDEX       62
-
-#endif /* _ASM_IA64_SOCKET_H */
index ad1185c..6b3ab58 100644 (file)
@@ -127,7 +127,7 @@ static struct fixed_phy_status nettel_fixed_phy_status __initdata = {
 static int __init init_BSP(void)
 {
        m5272_uarts_init();
-       fixed_phy_add(PHY_POLL, 0, &nettel_fixed_phy_status, -1);
+       fixed_phy_add(PHY_POLL, 0, &nettel_fixed_phy_status);
        return 0;
 }
 
index 3804935..40712e4 100644 (file)
@@ -155,18 +155,22 @@ out:
 static int __init nfhd_init(void)
 {
        u32 blocks, bsize;
+       int ret;
        int i;
 
        nfhd_id = nf_get_id("XHDI");
        if (!nfhd_id)
                return -ENODEV;
 
-       major_num = register_blkdev(major_num, "nfhd");
-       if (major_num <= 0) {
+       ret = register_blkdev(major_num, "nfhd");
+       if (ret < 0) {
                pr_warn("nfhd: unable to get major number\n");
-               return major_num;
+               return ret;
        }
 
+       if (!major_num)
+               major_num = ret;
+
        for (i = NFHD_DEV_OFFSET; i < 24; i++) {
                if (nfhd_get_capacity(i, 0, &blocks, &bsize))
                        continue;
index 9f1dd26..95f8f63 100644 (file)
@@ -20,6 +20,7 @@ generic-y += mm-arch-hooks.h
 generic-y += percpu.h
 generic-y += preempt.h
 generic-y += sections.h
+generic-y += shmparam.h
 generic-y += spinlock.h
 generic-y += topology.h
 generic-y += trace_clock.h
index b8b3525..960bf1e 100644 (file)
@@ -2,4 +2,3 @@ include include/uapi/asm-generic/Kbuild.asm
 
 generated-y += unistd_32.h
 generic-y += kvm_para.h
-generic-y += shmparam.h
index 9c7d1d2..791cc8d 100644 (file)
@@ -26,6 +26,7 @@ generic-y += parport.h
 generic-y += percpu.h
 generic-y += preempt.h
 generic-y += serial.h
+generic-y += shmparam.h
 generic-y += syscalls.h
 generic-y += topology.h
 generic-y += trace_clock.h
index 28823e3..97823ec 100644 (file)
@@ -2,5 +2,4 @@ include include/uapi/asm-generic/Kbuild.asm
 
 generated-y += unistd_32.h
 generic-y += kvm_para.h
-generic-y += shmparam.h
 generic-y += ucontext.h
index 0d14f51..a84c24d 100644 (file)
@@ -1403,6 +1403,21 @@ config LOONGSON3_ENHANCEMENT
          please say 'N' here. If you want a high-performance kernel to run on
          new Loongson 3 machines only, please say 'Y' here.
 
+config CPU_LOONGSON3_WORKAROUNDS
+       bool "Old Loongson 3 LLSC Workarounds"
+       default y if SMP
+       depends on CPU_LOONGSON3
+       help
+         Loongson 3 processors have the llsc issues which require workarounds.
+         Without workarounds the system may hang unexpectedly.
+
+         Newer Loongson 3 will fix these issues and no workarounds are needed.
+         The workarounds have no significant side effect on them but may
+         decrease the performance of the system so this option should be
+         disabled unless the kernel is intended to be run on old systems.
+
+         If unsure, please say Y.
+
 config CPU_LOONGSON2E
        bool "Loongson 2E"
        depends on SYS_HAS_CPU_LOONGSON2E
index f09262e..10ff07b 100644 (file)
@@ -683,7 +683,7 @@ static int __init ar7_register_devices(void)
 
        if (ar7_has_high_cpmac()) {
                res = fixed_phy_add(PHY_POLL, cpmac_high.id,
-                                   &fixed_phy_status, -1);
+                                   &fixed_phy_status);
                if (!res) {
                        cpmac_get_mac(1, cpmac_high_data.dev_addr);
 
@@ -696,7 +696,7 @@ static int __init ar7_register_devices(void)
        } else
                cpmac_low_data.phy_mask = 0xffffffff;
 
-       res = fixed_phy_add(PHY_POLL, cpmac_low.id, &fixed_phy_status, -1);
+       res = fixed_phy_add(PHY_POLL, cpmac_low.id, &fixed_phy_status);
        if (!res) {
                cpmac_get_mac(0, cpmac_low_data.dev_addr);
                res = platform_device_register(&cpmac_low);
index fe37735..82627c2 100644 (file)
@@ -274,7 +274,7 @@ static int __init bcm47xx_register_bus_complete(void)
        bcm47xx_leds_register();
        bcm47xx_workarounds();
 
-       fixed_phy_add(PHY_POLL, 0, &bcm47xx_fixed_phy_status, -1);
+       fixed_phy_add(PHY_POLL, 0, &bcm47xx_fixed_phy_status);
        return 0;
 }
 device_initcall(bcm47xx_register_bus_complete);
index 50cff3c..4f7b1fa 100644 (file)
@@ -76,7 +76,7 @@
        status = "okay";
 
        pinctrl-names = "default";
-       pinctrl-0 = <&pins_uart2>;
+       pinctrl-0 = <&pins_uart3>;
 };
 
 &uart4 {
                bias-disable;
        };
 
-       pins_uart2: uart2 {
-               function = "uart2";
-               groups = "uart2-data", "uart2-hwflow";
+       pins_uart3: uart3 {
+               function = "uart3";
+               groups = "uart3-data", "uart3-hwflow";
                bias-disable;
        };
 
index 6fb16fd..2beb78a 100644 (file)
                #dma-cells = <2>;
 
                interrupt-parent = <&intc>;
-               interrupts = <29>;
+               interrupts = <20>;
 
                clocks = <&cgu JZ4740_CLK_DMA>;
 
index 2152b7b..cc8dbea 100644 (file)
                interrupts = <0>;
        };
 
-       axi_i2c: i2c@10A00000 {
+       axi_i2c: i2c@10a00000 {
            compatible = "xlnx,xps-iic-2.00.a";
            interrupt-parent = <&axi_intc>;
            interrupts = <4>;
-           reg = < 0x10A00000 0x10000 >;
+           reg = < 0x10a00000 0x10000 >;
            clocks = <&ext>;
            xlnx,clk-freq = <0x5f5e100>;
            xlnx,family = "Artix7";
            #address-cells = <1>;
            #size-cells = <0>;
 
-           ad7420@4B {
+           ad7420@4b {
                compatible = "adi,adt7420";
-               reg = <0x4B>;
+               reg = <0x4b>;
            };
        } ;
 };
index 43fcd35..9409629 100644 (file)
@@ -58,6 +58,7 @@ static __inline__ void atomic_##op(int i, atomic_t * v)                             \
        if (kernel_uses_llsc) {                                               \
                int temp;                                                     \
                                                                              \
+               loongson_llsc_mb();                                           \
                __asm__ __volatile__(                                         \
                "       .set    push                                    \n"   \
                "       .set    "MIPS_ISA_LEVEL"                        \n"   \
@@ -85,6 +86,7 @@ static __inline__ int atomic_##op##_return_relaxed(int i, atomic_t * v)             \
        if (kernel_uses_llsc) {                                               \
                int temp;                                                     \
                                                                              \
+               loongson_llsc_mb();                                           \
                __asm__ __volatile__(                                         \
                "       .set    push                                    \n"   \
                "       .set    "MIPS_ISA_LEVEL"                        \n"   \
@@ -118,6 +120,7 @@ static __inline__ int atomic_fetch_##op##_relaxed(int i, atomic_t * v)            \
        if (kernel_uses_llsc) {                                               \
                int temp;                                                     \
                                                                              \
+               loongson_llsc_mb();                                           \
                __asm__ __volatile__(                                         \
                "       .set    push                                    \n"   \
                "       .set    "MIPS_ISA_LEVEL"                        \n"   \
@@ -256,6 +259,7 @@ static __inline__ void atomic64_##op(long i, atomic64_t * v)                      \
        if (kernel_uses_llsc) {                                               \
                long temp;                                                    \
                                                                              \
+               loongson_llsc_mb();                                           \
                __asm__ __volatile__(                                         \
                "       .set    push                                    \n"   \
                "       .set    "MIPS_ISA_LEVEL"                        \n"   \
@@ -283,6 +287,7 @@ static __inline__ long atomic64_##op##_return_relaxed(long i, atomic64_t * v) \
        if (kernel_uses_llsc) {                                               \
                long temp;                                                    \
                                                                              \
+               loongson_llsc_mb();                                           \
                __asm__ __volatile__(                                         \
                "       .set    push                                    \n"   \
                "       .set    "MIPS_ISA_LEVEL"                        \n"   \
@@ -316,6 +321,7 @@ static __inline__ long atomic64_fetch_##op##_relaxed(long i, atomic64_t * v)  \
        if (kernel_uses_llsc) {                                               \
                long temp;                                                    \
                                                                              \
+               loongson_llsc_mb();                                           \
                __asm__ __volatile__(                                         \
                "       .set    push                                    \n"   \
                "       .set    "MIPS_ISA_LEVEL"                        \n"   \
index a5eb1bb..b7f6ac5 100644 (file)
 #define __smp_mb__before_atomic()      __smp_mb__before_llsc()
 #define __smp_mb__after_atomic()       smp_llsc_mb()
 
+/*
+ * Some Loongson 3 CPUs have a bug wherein execution of a memory access (load,
+ * store or pref) in between an ll & sc can cause the sc instruction to
+ * erroneously succeed, breaking atomicity. Whilst it's unusual to write code
+ * containing such sequences, this bug bites harder than we might otherwise
+ * expect due to reordering & speculation:
+ *
+ * 1) A memory access appearing prior to the ll in program order may actually
+ *    be executed after the ll - this is the reordering case.
+ *
+ *    In order to avoid this we need to place a memory barrier (ie. a sync
+ *    instruction) prior to every ll instruction, in between it & any earlier
+ *    memory access instructions. Many of these cases are already covered by
+ *    smp_mb__before_llsc() but for the remaining cases, typically ones in
+ *    which multiple CPUs may operate on a memory location but ordering is not
+ *    usually guaranteed, we use loongson_llsc_mb() below.
+ *
+ *    This reordering case is fixed by 3A R2 CPUs, ie. 3A2000 models and later.
+ *
+ * 2) If a conditional branch exists between an ll & sc with a target outside
+ *    of the ll-sc loop, for example an exit upon value mismatch in cmpxchg()
+ *    or similar, then misprediction of the branch may allow speculative
+ *    execution of memory accesses from outside of the ll-sc loop.
+ *
+ *    In order to avoid this we need a memory barrier (ie. a sync instruction)
+ *    at each affected branch target, for which we also use loongson_llsc_mb()
+ *    defined below.
+ *
+ *    This case affects all current Loongson 3 CPUs.
+ */
+#ifdef CONFIG_CPU_LOONGSON3_WORKAROUNDS /* Loongson-3's LLSC workaround */
+#define loongson_llsc_mb()     __asm__ __volatile__(__WEAK_LLSC_MB : : :"memory")
+#else
+#define loongson_llsc_mb()     do { } while (0)
+#endif
+
 #include <asm-generic/barrier.h>
 
 #endif /* __ASM_BARRIER_H */
index c467595..830c93a 100644 (file)
@@ -69,6 +69,7 @@ static inline void set_bit(unsigned long nr, volatile unsigned long *addr)
                : "ir" (1UL << bit), GCC_OFF_SMALL_ASM() (*m));
 #if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6)
        } else if (kernel_uses_llsc && __builtin_constant_p(bit)) {
+               loongson_llsc_mb();
                do {
                        __asm__ __volatile__(
                        "       " __LL "%0, %1          # set_bit       \n"
@@ -79,6 +80,7 @@ static inline void set_bit(unsigned long nr, volatile unsigned long *addr)
                } while (unlikely(!temp));
 #endif /* CONFIG_CPU_MIPSR2 || CONFIG_CPU_MIPSR6 */
        } else if (kernel_uses_llsc) {
+               loongson_llsc_mb();
                do {
                        __asm__ __volatile__(
                        "       .set    push                            \n"
@@ -123,6 +125,7 @@ static inline void clear_bit(unsigned long nr, volatile unsigned long *addr)
                : "ir" (~(1UL << bit)));
 #if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6)
        } else if (kernel_uses_llsc && __builtin_constant_p(bit)) {
+               loongson_llsc_mb();
                do {
                        __asm__ __volatile__(
                        "       " __LL "%0, %1          # clear_bit     \n"
@@ -133,6 +136,7 @@ static inline void clear_bit(unsigned long nr, volatile unsigned long *addr)
                } while (unlikely(!temp));
 #endif /* CONFIG_CPU_MIPSR2 || CONFIG_CPU_MIPSR6 */
        } else if (kernel_uses_llsc) {
+               loongson_llsc_mb();
                do {
                        __asm__ __volatile__(
                        "       .set    push                            \n"
@@ -193,6 +197,7 @@ static inline void change_bit(unsigned long nr, volatile unsigned long *addr)
                unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
                unsigned long temp;
 
+               loongson_llsc_mb();
                do {
                        __asm__ __volatile__(
                        "       .set    push                            \n"
index c14d798..b83b039 100644 (file)
@@ -50,6 +50,7 @@
                  "i" (-EFAULT)                                         \
                : "memory");                                            \
        } else if (cpu_has_llsc) {                                      \
+               loongson_llsc_mb();                                     \
                __asm__ __volatile__(                                   \
                "       .set    push                            \n"     \
                "       .set    noat                            \n"     \
@@ -163,6 +164,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
                  "i" (-EFAULT)
                : "memory");
        } else if (cpu_has_llsc) {
+               loongson_llsc_mb();
                __asm__ __volatile__(
                "# futex_atomic_cmpxchg_inatomic                        \n"
                "       .set    push                                    \n"
@@ -192,6 +194,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
                : GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
                  "i" (-EFAULT)
                : "memory");
+               loongson_llsc_mb();
        } else
                return -ENOSYS;
 
index 57933fc..910851c 100644 (file)
@@ -228,6 +228,7 @@ static inline void set_pte(pte_t *ptep, pte_t pteval)
                        : [buddy] "+m" (buddy->pte), [tmp] "=&r" (tmp)
                        : [global] "r" (page_global));
                } else if (kernel_uses_llsc) {
+                       loongson_llsc_mb();
                        __asm__ __volatile__ (
                        "       .set    push                            \n"
                        "       .set    "MIPS_ISA_ARCH_LEVEL"           \n"
@@ -242,6 +243,7 @@ static inline void set_pte(pte_t *ptep, pte_t pteval)
                        "       .set    pop                             \n"
                        : [buddy] "+m" (buddy->pte), [tmp] "=&r" (tmp)
                        : [global] "r" (page_global));
+                       loongson_llsc_mb();
                }
 #else /* !CONFIG_SMP */
                if (pte_none(*buddy))
index 73e25e3..eb9f33f 100644 (file)
@@ -11,6 +11,7 @@
 #define _UAPI_ASM_SOCKET_H
 
 #include <asm/sockios.h>
+#include <asm/bitsperlong.h>
 
 /*
  * For setsockopt(2)
@@ -38,8 +39,8 @@
 #define SO_RCVBUF      0x1002  /* Receive buffer. */
 #define SO_SNDLOWAT    0x1003  /* send low-water mark */
 #define SO_RCVLOWAT    0x1004  /* receive low-water mark */
-#define SO_SNDTIMEO    0x1005  /* send timeout */
-#define SO_RCVTIMEO    0x1006  /* receive timeout */
+#define SO_SNDTIMEO_OLD        0x1005  /* send timeout */
+#define SO_RCVTIMEO_OLD        0x1006  /* receive timeout */
 #define SO_ACCEPTCONN  0x1009
 #define SO_PROTOCOL    0x1028  /* protocol type */
 #define SO_DOMAIN      0x1029  /* domain/socket family */
 #define SO_GET_FILTER          SO_ATTACH_FILTER
 
 #define SO_PEERNAME            28
-#define SO_TIMESTAMP           29
-#define SCM_TIMESTAMP          SO_TIMESTAMP
 
 #define SO_PEERSEC             30
 #define SO_SNDBUFFORCE         31
 #define SO_RCVBUFFORCE         33
 #define SO_PASSSEC             34
-#define SO_TIMESTAMPNS         35
-#define SCM_TIMESTAMPNS                SO_TIMESTAMPNS
 
 #define SO_MARK                        36
 
-#define SO_TIMESTAMPING                37
-#define SCM_TIMESTAMPING       SO_TIMESTAMPING
-
 #define SO_RXQ_OVFL            40
 
 #define SO_WIFI_STATUS         41
 
 #define SO_BINDTOIFINDEX       62
 
+#define SO_TIMESTAMP_OLD        29
+#define SO_TIMESTAMPNS_OLD      35
+#define SO_TIMESTAMPING_OLD     37
+
+#define SO_TIMESTAMP_NEW        63
+#define SO_TIMESTAMPNS_NEW      64
+#define SO_TIMESTAMPING_NEW     65
+
+#define SO_RCVTIMEO_NEW         66
+#define SO_SNDTIMEO_NEW         67
+
+#if !defined(__KERNEL__)
+
+#if __BITS_PER_LONG == 64
+#define SO_TIMESTAMP           SO_TIMESTAMP_OLD
+#define SO_TIMESTAMPNS         SO_TIMESTAMPNS_OLD
+#define SO_TIMESTAMPING                SO_TIMESTAMPING_OLD
+
+#define SO_RCVTIMEO             SO_RCVTIMEO_OLD
+#define SO_SNDTIMEO             SO_SNDTIMEO_OLD
+#else
+#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
+#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW)
+#define SO_TIMESTAMPING (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPING_OLD : SO_TIMESTAMPING_NEW)
+
+#define SO_RCVTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_RCVTIMEO_OLD : SO_RCVTIMEO_NEW)
+#define SO_SNDTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_SNDTIMEO_OLD : SO_SNDTIMEO_NEW)
+#endif
+
+#define SCM_TIMESTAMP           SO_TIMESTAMP
+#define SCM_TIMESTAMPNS         SO_TIMESTAMPNS
+#define SCM_TIMESTAMPING        SO_TIMESTAMPING
+
+#endif
+
 #endif /* _UAPI_ASM_SOCKET_H */
index 8f5bd04..7f3f136 100644 (file)
@@ -457,5 +457,5 @@ void mips_cm_error_report(void)
        }
 
        /* reprime cause register */
-       write_gcr_error_cause(0);
+       write_gcr_error_cause(cm_error);
 }
index 6829a06..339870e 100644 (file)
@@ -371,7 +371,7 @@ static inline int is_sp_move_ins(union mips_instruction *ip, int *frame_size)
 static int get_frame_info(struct mips_frame_info *info)
 {
        bool is_mmips = IS_ENABLED(CONFIG_CPU_MICROMIPS);
-       union mips_instruction insn, *ip, *ip_end;
+       union mips_instruction insn, *ip;
        const unsigned int max_insns = 128;
        unsigned int last_insn_size = 0;
        unsigned int i;
@@ -384,10 +384,9 @@ static int get_frame_info(struct mips_frame_info *info)
        if (!ip)
                goto err;
 
-       ip_end = (void *)ip + info->func_size;
-
-       for (i = 0; i < max_insns && ip < ip_end; i++) {
+       for (i = 0; i < max_insns; i++) {
                ip = (void *)ip + last_insn_size;
+
                if (is_mmips && mm_insn_16bit(ip->halfword[0])) {
                        insn.word = ip->halfword[0] << 16;
                        last_insn_size = 2;
index 0fce460..c1a4d4d 100644 (file)
@@ -23,6 +23,29 @@ ifdef CONFIG_CPU_LOONGSON2F_WORKAROUNDS
 endif
 
 cflags-$(CONFIG_CPU_LOONGSON3) += -Wa,--trap
+
+#
+# Some versions of binutils, not currently mainline as of 2019/02/04, support
+# an -mfix-loongson3-llsc flag which emits a sync prior to each ll instruction
+# to work around a CPU bug (see loongson_llsc_mb() in asm/barrier.h for a
+# description).
+#
+# We disable this in order to prevent the assembler meddling with the
+# instruction that labels refer to, ie. if we label an ll instruction:
+#
+# 1: ll v0, 0(a0)
+#
+# ...then with the assembler fix applied the label may actually point at a sync
+# instruction inserted by the assembler, and if we were using the label in an
+# exception table the table would no longer contain the address of the ll
+# instruction.
+#
+# Avoid this by explicitly disabling that assembler behaviour. If upstream
+# binutils does not merge support for the flag then we can revisit & remove
+# this later - for now it ensures vendor toolchains don't cause problems.
+#
+cflags-$(CONFIG_CPU_LOONGSON3) += $(call as-option,-Wa$(comma)-mno-fix-loongson3-llsc,)
+
 #
 # binutils from v2.25 on and gcc starting from v4.9.0 treat -march=loongson3a
 # as MIPS64 R2; older versions as just R1.  This leaves the possibility open
index a60715e..b26892c 100644 (file)
@@ -59,7 +59,12 @@ static void loongson_poweroff(void)
 {
 #ifndef CONFIG_LEFI_FIRMWARE_INTERFACE
        mach_prepare_shutdown();
-       unreachable();
+
+       /*
+        * It needs a wait loop here, but mips/kernel/reset.c already calls
+        * a generic delay loop, machine_hang(), so simply return.
+        */
+       return;
 #else
        void (*fw_poweroff)(void) = (void *)loongson_sysconf.poweroff_addr;
 
index 37b1cb2..65b6e85 100644 (file)
@@ -932,6 +932,8 @@ build_get_pgd_vmalloc64(u32 **p, struct uasm_label **l, struct uasm_reloc **r,
                 * to mimic that here by taking a load/istream page
                 * fault.
                 */
+               if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS))
+                       uasm_i_sync(p, 0);
                UASM_i_LA(p, ptr, (unsigned long)tlb_do_page_fault_0);
                uasm_i_jr(p, ptr);
 
@@ -1646,6 +1648,8 @@ static void
 iPTE_LW(u32 **p, unsigned int pte, unsigned int ptr)
 {
 #ifdef CONFIG_SMP
+       if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS))
+               uasm_i_sync(p, 0);
 # ifdef CONFIG_PHYS_ADDR_T_64BIT
        if (cpu_has_64bits)
                uasm_i_lld(p, pte, 0, ptr);
@@ -2259,6 +2263,8 @@ static void build_r4000_tlb_load_handler(void)
 #endif
 
        uasm_l_nopage_tlbl(&l, p);
+       if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS))
+               uasm_i_sync(&p, 0);
        build_restore_work_registers(&p);
 #ifdef CONFIG_CPU_MICROMIPS
        if ((unsigned long)tlb_do_page_fault_0 & 1) {
@@ -2313,6 +2319,8 @@ static void build_r4000_tlb_store_handler(void)
 #endif
 
        uasm_l_nopage_tlbs(&l, p);
+       if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS))
+               uasm_i_sync(&p, 0);
        build_restore_work_registers(&p);
 #ifdef CONFIG_CPU_MICROMIPS
        if ((unsigned long)tlb_do_page_fault_1 & 1) {
@@ -2368,6 +2376,8 @@ static void build_r4000_tlb_modify_handler(void)
 #endif
 
        uasm_l_nopage_tlbm(&l, p);
+       if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS))
+               uasm_i_sync(&p, 0);
        build_restore_work_registers(&p);
 #ifdef CONFIG_CPU_MICROMIPS
        if ((unsigned long)tlb_do_page_fault_1 & 1) {
index 5017d58..fc29b85 100644 (file)
@@ -568,6 +568,11 @@ static int __init octeon_pci_setup(void)
        if (octeon_has_feature(OCTEON_FEATURE_PCIE))
                return 0;
 
+       if (!octeon_is_pci_host()) {
+               pr_notice("Not in host mode, PCI Controller not initialized\n");
+               return 0;
+       }
+
        /* Point pcibios_map_irq() to the PCI version of it */
        octeon_pcibios_map_irq = octeon_pci_pcibios_map_irq;
 
@@ -579,11 +584,6 @@ static int __init octeon_pci_setup(void)
        else
                octeon_dma_bar_type = OCTEON_DMA_BAR_TYPE_BIG;
 
-       if (!octeon_is_pci_host()) {
-               pr_notice("Not in host mode, PCI Controller not initialized\n");
-               return 0;
-       }
-
        /* PCI I/O and PCI MEM values */
        set_io_port_base(OCTEON_PCI_IOSPACE_BASE);
        ioport_resource.start = 0;
index f6fd340..0ede4de 100644 (file)
@@ -8,6 +8,7 @@ ccflags-vdso := \
        $(filter -E%,$(KBUILD_CFLAGS)) \
        $(filter -mmicromips,$(KBUILD_CFLAGS)) \
        $(filter -march=%,$(KBUILD_CFLAGS)) \
+       $(filter -m%-float,$(KBUILD_CFLAGS)) \
        -D__VDSO__
 
 ifdef CONFIG_CC_IS_CLANG
@@ -129,7 +130,7 @@ $(obj)/%-o32.o: $(src)/%.c FORCE
        $(call cmd,force_checksrc)
        $(call if_changed_rule,cc_o_c)
 
-$(obj)/vdso-o32.lds: KBUILD_CPPFLAGS := -mabi=32
+$(obj)/vdso-o32.lds: KBUILD_CPPFLAGS := $(ccflags-vdso) -mabi=32
 $(obj)/vdso-o32.lds: $(src)/vdso.lds.S FORCE
        $(call if_changed_dep,cpp_lds_S)
 
@@ -169,7 +170,7 @@ $(obj)/%-n32.o: $(src)/%.c FORCE
        $(call cmd,force_checksrc)
        $(call if_changed_rule,cc_o_c)
 
-$(obj)/vdso-n32.lds: KBUILD_CPPFLAGS := -mabi=n32
+$(obj)/vdso-n32.lds: KBUILD_CPPFLAGS := $(ccflags-vdso) -mabi=n32
 $(obj)/vdso-n32.lds: $(src)/vdso.lds.S FORCE
        $(call if_changed_dep,cpp_lds_S)
 
index eb87cd8..1f04844 100644 (file)
@@ -34,6 +34,7 @@ generic-y += qrwlock_types.h
 generic-y += qrwlock.h
 generic-y += sections.h
 generic-y += segment.h
+generic-y += shmparam.h
 generic-y += string.h
 generic-y += switch_to.h
 generic-y += topology.h
index 6c6f630..0febf1a 100644 (file)
@@ -1,5 +1,4 @@
 include include/uapi/asm-generic/Kbuild.asm
 
 generic-y += kvm_para.h
-generic-y += shmparam.h
 generic-y += ucontext.h
index 52bed59..16e428f 100644 (file)
@@ -3,6 +3,7 @@
 #define _UAPI_ASM_SOCKET_H
 
 #include <asm/sockios.h>
+#include <asm/bitsperlong.h>
 
 /* For setsockopt(2) */
 #define SOL_SOCKET     0xffff
@@ -21,8 +22,8 @@
 #define SO_RCVBUFFORCE 0x100b
 #define SO_SNDLOWAT    0x1003
 #define SO_RCVLOWAT    0x1004
-#define SO_SNDTIMEO    0x1005
-#define SO_RCVTIMEO    0x1006
+#define SO_SNDTIMEO_OLD        0x1005
+#define SO_RCVTIMEO_OLD        0x1006
 #define SO_ERROR       0x1007
 #define SO_TYPE                0x1008
 #define SO_PROTOCOL    0x1028
 #define SO_BSDCOMPAT   0x400e
 #define SO_PASSCRED    0x4010
 #define SO_PEERCRED    0x4011
-#define SO_TIMESTAMP   0x4012
-#define SCM_TIMESTAMP  SO_TIMESTAMP
-#define SO_TIMESTAMPNS 0x4013
-#define SCM_TIMESTAMPNS        SO_TIMESTAMPNS
 
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x4016
@@ -58,9 +55,6 @@
 
 #define SO_MARK                        0x401f
 
-#define SO_TIMESTAMPING                0x4020
-#define SCM_TIMESTAMPING       SO_TIMESTAMPING
-
 #define SO_RXQ_OVFL             0x4021
 
 #define SO_WIFI_STATUS         0x4022
 
 #define SO_BINDTOIFINDEX       0x4037
 
+#define SO_TIMESTAMP_OLD        0x4012
+#define SO_TIMESTAMPNS_OLD      0x4013
+#define SO_TIMESTAMPING_OLD     0x4020
+
+#define SO_TIMESTAMP_NEW        0x4038
+#define SO_TIMESTAMPNS_NEW      0x4039
+#define SO_TIMESTAMPING_NEW     0x403A
+
+#define SO_RCVTIMEO_NEW         0x4040
+#define SO_SNDTIMEO_NEW         0x4041
+
+#if !defined(__KERNEL__)
+
+#if __BITS_PER_LONG == 64
+#define SO_TIMESTAMP           SO_TIMESTAMP_OLD
+#define SO_TIMESTAMPNS         SO_TIMESTAMPNS_OLD
+#define SO_TIMESTAMPING         SO_TIMESTAMPING_OLD
+#define SO_RCVTIMEO            SO_RCVTIMEO_OLD
+#define SO_SNDTIMEO            SO_SNDTIMEO_OLD
+#else
+#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
+#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW)
+#define SO_TIMESTAMPING (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPING_OLD : SO_TIMESTAMPING_NEW)
+
+#define SO_RCVTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_RCVTIMEO_OLD : SO_RCVTIMEO_NEW)
+#define SO_SNDTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_SNDTIMEO_OLD : SO_SNDTIMEO_NEW)
+#endif
+
+#define SCM_TIMESTAMP           SO_TIMESTAMP
+#define SCM_TIMESTAMPNS         SO_TIMESTAMPNS
+#define SCM_TIMESTAMPING        SO_TIMESTAMPING
+
+#endif
+
 #endif /* _UAPI_ASM_SOCKET_H */
index 2e6ada2..c9bfe52 100644 (file)
@@ -1258,21 +1258,13 @@ extern pmd_t pmdp_invalidate(struct vm_area_struct *vma, unsigned long address,
 
 #define pmd_move_must_withdraw pmd_move_must_withdraw
 struct spinlock;
-static inline int pmd_move_must_withdraw(struct spinlock *new_pmd_ptl,
-                                        struct spinlock *old_pmd_ptl,
-                                        struct vm_area_struct *vma)
-{
-       if (radix_enabled())
-               return false;
-       /*
-        * Archs like ppc64 use pgtable to store per pmd
-        * specific information. So when we switch the pmd,
-        * we should also withdraw and deposit the pgtable
-        */
-       return true;
-}
-
-
+extern int pmd_move_must_withdraw(struct spinlock *new_pmd_ptl,
+                                 struct spinlock *old_pmd_ptl,
+                                 struct vm_area_struct *vma);
+/*
+ * Hash translation mode use the deposited table to store hash pte
+ * slot information.
+ */
 #define arch_needs_pgtable_deposit arch_needs_pgtable_deposit
 static inline bool arch_needs_pgtable_deposit(void)
 {
index 94de465..12aa0c4 100644 (file)
@@ -11,8 +11,8 @@
 
 #define SO_RCVLOWAT    16
 #define SO_SNDLOWAT    17
-#define SO_RCVTIMEO    18
-#define SO_SNDTIMEO    19
+#define SO_RCVTIMEO_OLD        18
+#define SO_SNDTIMEO_OLD        19
 #define SO_PASSCRED    20
 #define SO_PEERCRED    21
 
index f3c31f5..ecd3156 100644 (file)
@@ -400,3 +400,25 @@ void arch_report_meminfo(struct seq_file *m)
                   atomic_long_read(&direct_pages_count[MMU_PAGE_1G]) << 20);
 }
 #endif /* CONFIG_PROC_FS */
+
+/*
+ * For hash translation mode, we use the deposited table to store hash slot
+ * information and they are stored at PTRS_PER_PMD offset from related pmd
+ * location. Hence a pmd move requires deposit and withdraw.
+ *
+ * For radix translation with split pmd ptl, we store the deposited table in the
+ * pmd page. Hence if we have different pmd page we need to withdraw during pmd
+ * move.
+ *
+ * With hash we use deposited table always irrespective of anon or not.
+ * With radix we use deposited table only for anonymous mapping.
+ */
+int pmd_move_must_withdraw(struct spinlock *new_pmd_ptl,
+                          struct spinlock *old_pmd_ptl,
+                          struct vm_area_struct *vma)
+{
+       if (radix_enabled())
+               return (new_pmd_ptl != old_pmd_ptl) && vma_is_anonymous(vma);
+
+       return true;
+}
index 15bba76..4194d3c 100644 (file)
@@ -1185,6 +1185,7 @@ skip_codegen_passes:
 
        bpf_flush_icache(bpf_hdr, (u8 *)bpf_hdr + (bpf_hdr->pages * PAGE_SIZE));
        if (!fp->is_func || extra_pass) {
+               bpf_prog_fill_jited_linfo(fp, addrs);
 out_addrs:
                kfree(addrs);
                kfree(jit_data);
index 7d6457a..bba281b 100644 (file)
@@ -43,6 +43,7 @@ static int drc_pmem_bind(struct papr_scm_priv *p)
 {
        unsigned long ret[PLPAR_HCALL_BUFSIZE];
        uint64_t rc, token;
+       uint64_t saved = 0;
 
        /*
         * When the hypervisor cannot map all the requested memory in a single
@@ -56,6 +57,8 @@ static int drc_pmem_bind(struct papr_scm_priv *p)
                rc = plpar_hcall(H_SCM_BIND_MEM, ret, p->drc_index, 0,
                                p->blocks, BIND_ANY_ADDR, token);
                token = ret[0];
+               if (!saved)
+                       saved = ret[1];
                cond_resched();
        } while (rc == H_BUSY);
 
@@ -64,7 +67,7 @@ static int drc_pmem_bind(struct papr_scm_priv *p)
                return -ENXIO;
        }
 
-       p->bound_addr = ret[1];
+       p->bound_addr = saved;
 
        dev_dbg(&p->pdev->dev, "bound drc %x to %pR\n", p->drc_index, &p->res);
 
index feeeaa6..bd14990 100644 (file)
@@ -49,6 +49,7 @@ config RISCV
        select RISCV_TIMER
        select GENERIC_IRQ_MULTI_HANDLER
        select ARCH_HAS_PTE_SPECIAL
+       select HAVE_EBPF_JIT if 64BIT
 
 config MMU
        def_bool y
@@ -103,7 +104,7 @@ choice
        prompt "Base ISA"
        default ARCH_RV64I
        help
-         This selects the base ISA that this kernel will traget and must match
+         This selects the base ISA that this kernel will target and must match
          the target platform.
 
 config ARCH_RV32I
index 4b594f2..c6342e6 100644 (file)
@@ -77,7 +77,7 @@ KBUILD_IMAGE  := $(boot)/Image.gz
 
 head-y := arch/riscv/kernel/head.o
 
-core-y += arch/riscv/kernel/ arch/riscv/mm/
+core-y += arch/riscv/kernel/ arch/riscv/mm/ arch/riscv/net/
 
 libs-y += arch/riscv/lib/
 
index f399659..2fd3461 100644 (file)
@@ -13,8 +13,6 @@ CONFIG_BLK_DEV_INITRD=y
 CONFIG_EXPERT=y
 CONFIG_BPF_SYSCALL=y
 CONFIG_SMP=y
-CONFIG_PCI=y
-CONFIG_PCIE_XILINX=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_NET=y
@@ -28,6 +26,10 @@ CONFIG_IP_PNP_DHCP=y
 CONFIG_IP_PNP_BOOTP=y
 CONFIG_IP_PNP_RARP=y
 CONFIG_NETLINK_DIAG=y
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCI_HOST_GENERIC=y
+CONFIG_PCIE_XILINX=y
 CONFIG_DEVTMPFS=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_VIRTIO_BLK=y
@@ -63,7 +65,6 @@ CONFIG_USB_STORAGE=y
 CONFIG_USB_UAS=y
 CONFIG_VIRTIO_MMIO=y
 CONFIG_SIFIVE_PLIC=y
-CONFIG_RAS=y
 CONFIG_EXT4_FS=y
 CONFIG_EXT4_FS_POSIX_ACL=y
 CONFIG_AUTOFS4_FS=y
@@ -77,5 +78,6 @@ CONFIG_NFS_V4_1=y
 CONFIG_NFS_V4_2=y
 CONFIG_ROOT_NFS=y
 CONFIG_CRYPTO_USER_API_HASH=y
+CONFIG_CRYPTO_DEV_VIRTIO=y
 CONFIG_PRINTK_TIME=y
 # CONFIG_RCU_TRACE is not set
index 06cfbb3..2a546a5 100644 (file)
@@ -80,7 +80,7 @@ typedef struct page *pgtable_t;
 #define __pgd(x)       ((pgd_t) { (x) })
 #define __pgprot(x)    ((pgprot_t) { (x) })
 
-#ifdef CONFIG_64BITS
+#ifdef CONFIG_64BIT
 #define PTE_FMT "%016lx"
 #else
 #define PTE_FMT "%08lx"
index 2fa2942..470755c 100644 (file)
 #define _PAGE_SPECIAL   _PAGE_SOFT
 #define _PAGE_TABLE     _PAGE_PRESENT
 
+/*
+ * _PAGE_PROT_NONE is set on not-present pages (and ignored by the hardware) to
+ * distinguish them from swapped out pages
+ */
+#define _PAGE_PROT_NONE _PAGE_READ
+
 #define _PAGE_PFN_SHIFT 10
 
 /* Set of bits to preserve across pte_modify() */
index 1630196..a8179a8 100644 (file)
@@ -44,7 +44,7 @@
 /* Page protection bits */
 #define _PAGE_BASE     (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_USER)
 
-#define PAGE_NONE              __pgprot(0)
+#define PAGE_NONE              __pgprot(_PAGE_PROT_NONE)
 #define PAGE_READ              __pgprot(_PAGE_BASE | _PAGE_READ)
 #define PAGE_WRITE             __pgprot(_PAGE_BASE | _PAGE_READ | _PAGE_WRITE)
 #define PAGE_EXEC              __pgprot(_PAGE_BASE | _PAGE_EXEC)
@@ -98,7 +98,7 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
 
 static inline int pmd_present(pmd_t pmd)
 {
-       return (pmd_val(pmd) & _PAGE_PRESENT);
+       return (pmd_val(pmd) & (_PAGE_PRESENT | _PAGE_PROT_NONE));
 }
 
 static inline int pmd_none(pmd_t pmd)
@@ -178,7 +178,7 @@ static inline pte_t *pte_offset_kernel(pmd_t *pmd, unsigned long addr)
 
 static inline int pte_present(pte_t pte)
 {
-       return (pte_val(pte) & _PAGE_PRESENT);
+       return (pte_val(pte) & (_PAGE_PRESENT | _PAGE_PROT_NONE));
 }
 
 static inline int pte_none(pte_t pte)
@@ -380,7 +380,7 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
  *
  * Format of swap PTE:
  *     bit            0:       _PAGE_PRESENT (zero)
- *     bit            1:       reserved for future use (zero)
+ *     bit            1:       _PAGE_PROT_NONE (zero)
  *     bits      2 to 6:       swap type
  *     bits 7 to XLEN-1:       swap offset
  */
index 0531f49..ce70bce 100644 (file)
@@ -22,7 +22,7 @@
  * This decides where the kernel will search for a free chunk of vm
  * space during mmap's.
  */
-#define TASK_UNMAPPED_BASE     PAGE_ALIGN(TASK_SIZE >> 1)
+#define TASK_UNMAPPED_BASE     PAGE_ALIGN(TASK_SIZE / 3)
 
 #define STACK_TOP              TASK_SIZE
 #define STACK_TOP_MAX          STACK_TOP
index 6a92a2f..dac9834 100644 (file)
@@ -39,6 +39,7 @@ void asm_offsets(void)
        OFFSET(TASK_STACK, task_struct, stack);
        OFFSET(TASK_TI, task_struct, thread_info);
        OFFSET(TASK_TI_FLAGS, task_struct, thread_info.flags);
+       OFFSET(TASK_TI_PREEMPT_COUNT, task_struct, thread_info.preempt_count);
        OFFSET(TASK_TI_KERNEL_SP, task_struct, thread_info.kernel_sp);
        OFFSET(TASK_TI_USER_SP, task_struct, thread_info.user_sp);
        OFFSET(TASK_TI_CPU, task_struct, thread_info.cpu);
index 355166f..fd9b57c 100644 (file)
@@ -144,6 +144,10 @@ _save_context:
        REG_L x2,  PT_SP(sp)
        .endm
 
+#if !IS_ENABLED(CONFIG_PREEMPT)
+.set resume_kernel, restore_all
+#endif
+
 ENTRY(handle_exception)
        SAVE_ALL
 
@@ -228,7 +232,7 @@ ret_from_exception:
        REG_L s0, PT_SSTATUS(sp)
        csrc sstatus, SR_SIE
        andi s0, s0, SR_SPP
-       bnez s0, restore_all
+       bnez s0, resume_kernel
 
 resume_userspace:
        /* Interrupts must be disabled here so flags are checked atomically */
@@ -250,6 +254,18 @@ restore_all:
        RESTORE_ALL
        sret
 
+#if IS_ENABLED(CONFIG_PREEMPT)
+resume_kernel:
+       REG_L s0, TASK_TI_PREEMPT_COUNT(tp)
+       bnez s0, restore_all
+need_resched:
+       REG_L s0, TASK_TI_FLAGS(tp)
+       andi s0, s0, _TIF_NEED_RESCHED
+       beqz s0, restore_all
+       call preempt_schedule_irq
+       j need_resched
+#endif
+
 work_pending:
        /* Enter slow path for supplementary processing */
        la ra, ret_from_exception
index 6e079e9..7756431 100644 (file)
@@ -181,7 +181,7 @@ static void __init setup_bootmem(void)
        BUG_ON(mem_size == 0);
 
        set_max_mapnr(PFN_DOWN(mem_size));
-       max_low_pfn = memblock_end_of_DRAM();
+       max_low_pfn = PFN_DOWN(memblock_end_of_DRAM());
 
 #ifdef CONFIG_BLK_DEV_INITRD
        setup_initrd();
index fc185ec..18cda0e 100644 (file)
@@ -57,15 +57,12 @@ void __init setup_smp(void)
 
        while ((dn = of_find_node_by_type(dn, "cpu"))) {
                hart = riscv_of_processor_hartid(dn);
-               if (hart < 0) {
-                       of_node_put(dn);
+               if (hart < 0)
                        continue;
-               }
 
                if (hart == cpuid_to_hartid_map(0)) {
                        BUG_ON(found_boot_cpu);
                        found_boot_cpu = 1;
-                       of_node_put(dn);
                        continue;
                }
 
@@ -73,7 +70,6 @@ void __init setup_smp(void)
                set_cpu_possible(cpuid, true);
                set_cpu_present(cpuid, true);
                cpuid++;
-               of_node_put(dn);
        }
 
        BUG_ON(!found_boot_cpu);
index 1e1395d..65df1df 100644 (file)
@@ -18,8 +18,6 @@
 #include <asm/cache.h>
 #include <asm/thread_info.h>
 
-#define MAX_BYTES_PER_LONG     0x10
-
 OUTPUT_ARCH(riscv)
 ENTRY(_start)
 
@@ -76,6 +74,8 @@ SECTIONS
                *(.sbss*)
        }
 
+       BSS_SECTION(PAGE_SIZE, PAGE_SIZE, 0)
+
        EXCEPTION_TABLE(0x10)
        NOTES
 
@@ -83,10 +83,6 @@ SECTIONS
                *(.rel.dyn*)
        }
 
-       BSS_SECTION(MAX_BYTES_PER_LONG,
-                   MAX_BYTES_PER_LONG,
-                   MAX_BYTES_PER_LONG)
-
        _end = .;
 
        STABS_DEBUG
index 1d9bfaf..658ebf6 100644 (file)
@@ -28,7 +28,8 @@ static void __init zone_sizes_init(void)
        unsigned long max_zone_pfns[MAX_NR_ZONES] = { 0, };
 
 #ifdef CONFIG_ZONE_DMA32
-       max_zone_pfns[ZONE_DMA32] = PFN_DOWN(min(4UL * SZ_1G, max_low_pfn));
+       max_zone_pfns[ZONE_DMA32] = PFN_DOWN(min(4UL * SZ_1G,
+                       (unsigned long) PFN_PHYS(max_low_pfn)));
 #endif
        max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
 
diff --git a/arch/riscv/net/Makefile b/arch/riscv/net/Makefile
new file mode 100644 (file)
index 0000000..a132220
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o
diff --git a/arch/riscv/net/bpf_jit_comp.c b/arch/riscv/net/bpf_jit_comp.c
new file mode 100644 (file)
index 0000000..80b12aa
--- /dev/null
@@ -0,0 +1,1602 @@
+// SPDX-License-Identifier: GPL-2.0
+/* BPF JIT compiler for RV64G
+ *
+ * Copyright(c) 2019 Björn Töpel <bjorn.topel@gmail.com>
+ *
+ */
+
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <asm/cacheflush.h>
+
+enum {
+       RV_REG_ZERO =   0,      /* The constant value 0 */
+       RV_REG_RA =     1,      /* Return address */
+       RV_REG_SP =     2,      /* Stack pointer */
+       RV_REG_GP =     3,      /* Global pointer */
+       RV_REG_TP =     4,      /* Thread pointer */
+       RV_REG_T0 =     5,      /* Temporaries */
+       RV_REG_T1 =     6,
+       RV_REG_T2 =     7,
+       RV_REG_FP =     8,
+       RV_REG_S1 =     9,      /* Saved registers */
+       RV_REG_A0 =     10,     /* Function argument/return values */
+       RV_REG_A1 =     11,     /* Function arguments */
+       RV_REG_A2 =     12,
+       RV_REG_A3 =     13,
+       RV_REG_A4 =     14,
+       RV_REG_A5 =     15,
+       RV_REG_A6 =     16,
+       RV_REG_A7 =     17,
+       RV_REG_S2 =     18,     /* Saved registers */
+       RV_REG_S3 =     19,
+       RV_REG_S4 =     20,
+       RV_REG_S5 =     21,
+       RV_REG_S6 =     22,
+       RV_REG_S7 =     23,
+       RV_REG_S8 =     24,
+       RV_REG_S9 =     25,
+       RV_REG_S10 =    26,
+       RV_REG_S11 =    27,
+       RV_REG_T3 =     28,     /* Temporaries */
+       RV_REG_T4 =     29,
+       RV_REG_T5 =     30,
+       RV_REG_T6 =     31,
+};
+
+#define RV_REG_TCC RV_REG_A6
+#define RV_REG_TCC_SAVED RV_REG_S6 /* Store A6 in S6 if program do calls */
+
+static const int regmap[] = {
+       [BPF_REG_0] =   RV_REG_A5,
+       [BPF_REG_1] =   RV_REG_A0,
+       [BPF_REG_2] =   RV_REG_A1,
+       [BPF_REG_3] =   RV_REG_A2,
+       [BPF_REG_4] =   RV_REG_A3,
+       [BPF_REG_5] =   RV_REG_A4,
+       [BPF_REG_6] =   RV_REG_S1,
+       [BPF_REG_7] =   RV_REG_S2,
+       [BPF_REG_8] =   RV_REG_S3,
+       [BPF_REG_9] =   RV_REG_S4,
+       [BPF_REG_FP] =  RV_REG_S5,
+       [BPF_REG_AX] =  RV_REG_T0,
+};
+
+enum {
+       RV_CTX_F_SEEN_TAIL_CALL =       0,
+       RV_CTX_F_SEEN_CALL =            RV_REG_RA,
+       RV_CTX_F_SEEN_S1 =              RV_REG_S1,
+       RV_CTX_F_SEEN_S2 =              RV_REG_S2,
+       RV_CTX_F_SEEN_S3 =              RV_REG_S3,
+       RV_CTX_F_SEEN_S4 =              RV_REG_S4,
+       RV_CTX_F_SEEN_S5 =              RV_REG_S5,
+       RV_CTX_F_SEEN_S6 =              RV_REG_S6,
+};
+
+struct rv_jit_context {
+       struct bpf_prog *prog;
+       u32 *insns; /* RV insns */
+       int ninsns;
+       int epilogue_offset;
+       int *offset; /* BPF to RV */
+       unsigned long flags;
+       int stack_size;
+};
+
+struct rv_jit_data {
+       struct bpf_binary_header *header;
+       u8 *image;
+       struct rv_jit_context ctx;
+};
+
+static u8 bpf_to_rv_reg(int bpf_reg, struct rv_jit_context *ctx)
+{
+       u8 reg = regmap[bpf_reg];
+
+       switch (reg) {
+       case RV_CTX_F_SEEN_S1:
+       case RV_CTX_F_SEEN_S2:
+       case RV_CTX_F_SEEN_S3:
+       case RV_CTX_F_SEEN_S4:
+       case RV_CTX_F_SEEN_S5:
+       case RV_CTX_F_SEEN_S6:
+               __set_bit(reg, &ctx->flags);
+       }
+       return reg;
+};
+
+static bool seen_reg(int reg, struct rv_jit_context *ctx)
+{
+       switch (reg) {
+       case RV_CTX_F_SEEN_CALL:
+       case RV_CTX_F_SEEN_S1:
+       case RV_CTX_F_SEEN_S2:
+       case RV_CTX_F_SEEN_S3:
+       case RV_CTX_F_SEEN_S4:
+       case RV_CTX_F_SEEN_S5:
+       case RV_CTX_F_SEEN_S6:
+               return test_bit(reg, &ctx->flags);
+       }
+       return false;
+}
+
+static void mark_call(struct rv_jit_context *ctx)
+{
+       __set_bit(RV_CTX_F_SEEN_CALL, &ctx->flags);
+}
+
+static bool seen_call(struct rv_jit_context *ctx)
+{
+       return test_bit(RV_CTX_F_SEEN_CALL, &ctx->flags);
+}
+
+static void mark_tail_call(struct rv_jit_context *ctx)
+{
+       __set_bit(RV_CTX_F_SEEN_TAIL_CALL, &ctx->flags);
+}
+
+static bool seen_tail_call(struct rv_jit_context *ctx)
+{
+       return test_bit(RV_CTX_F_SEEN_TAIL_CALL, &ctx->flags);
+}
+
+static u8 rv_tail_call_reg(struct rv_jit_context *ctx)
+{
+       mark_tail_call(ctx);
+
+       if (seen_call(ctx)) {
+               __set_bit(RV_CTX_F_SEEN_S6, &ctx->flags);
+               return RV_REG_S6;
+       }
+       return RV_REG_A6;
+}
+
+static void emit(const u32 insn, struct rv_jit_context *ctx)
+{
+       if (ctx->insns)
+               ctx->insns[ctx->ninsns] = insn;
+
+       ctx->ninsns++;
+}
+
+static u32 rv_r_insn(u8 funct7, u8 rs2, u8 rs1, u8 funct3, u8 rd, u8 opcode)
+{
+       return (funct7 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) |
+               (rd << 7) | opcode;
+}
+
+static u32 rv_i_insn(u16 imm11_0, u8 rs1, u8 funct3, u8 rd, u8 opcode)
+{
+       return (imm11_0 << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) |
+               opcode;
+}
+
+static u32 rv_s_insn(u16 imm11_0, u8 rs2, u8 rs1, u8 funct3, u8 opcode)
+{
+       u8 imm11_5 = imm11_0 >> 5, imm4_0 = imm11_0 & 0x1f;
+
+       return (imm11_5 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) |
+               (imm4_0 << 7) | opcode;
+}
+
+static u32 rv_sb_insn(u16 imm12_1, u8 rs2, u8 rs1, u8 funct3, u8 opcode)
+{
+       u8 imm12 = ((imm12_1 & 0x800) >> 5) | ((imm12_1 & 0x3f0) >> 4);
+       u8 imm4_1 = ((imm12_1 & 0xf) << 1) | ((imm12_1 & 0x400) >> 10);
+
+       return (imm12 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) |
+               (imm4_1 << 7) | opcode;
+}
+
+static u32 rv_u_insn(u32 imm31_12, u8 rd, u8 opcode)
+{
+       return (imm31_12 << 12) | (rd << 7) | opcode;
+}
+
+static u32 rv_uj_insn(u32 imm20_1, u8 rd, u8 opcode)
+{
+       u32 imm;
+
+       imm = (imm20_1 & 0x80000) |  ((imm20_1 & 0x3ff) << 9) |
+             ((imm20_1 & 0x400) >> 2) | ((imm20_1 & 0x7f800) >> 11);
+
+       return (imm << 12) | (rd << 7) | opcode;
+}
+
+static u32 rv_amo_insn(u8 funct5, u8 aq, u8 rl, u8 rs2, u8 rs1,
+                      u8 funct3, u8 rd, u8 opcode)
+{
+       u8 funct7 = (funct5 << 2) | (aq << 1) | rl;
+
+       return rv_r_insn(funct7, rs2, rs1, funct3, rd, opcode);
+}
+
+static u32 rv_addiw(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(imm11_0, rs1, 0, rd, 0x1b);
+}
+
+static u32 rv_addi(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(imm11_0, rs1, 0, rd, 0x13);
+}
+
+static u32 rv_addw(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0, rs2, rs1, 0, rd, 0x3b);
+}
+
+static u32 rv_add(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0, rs2, rs1, 0, rd, 0x33);
+}
+
+static u32 rv_subw(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0x20, rs2, rs1, 0, rd, 0x3b);
+}
+
+static u32 rv_sub(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0x20, rs2, rs1, 0, rd, 0x33);
+}
+
+static u32 rv_and(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0, rs2, rs1, 7, rd, 0x33);
+}
+
+static u32 rv_or(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0, rs2, rs1, 6, rd, 0x33);
+}
+
+static u32 rv_xor(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0, rs2, rs1, 4, rd, 0x33);
+}
+
+static u32 rv_mulw(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(1, rs2, rs1, 0, rd, 0x3b);
+}
+
+static u32 rv_mul(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(1, rs2, rs1, 0, rd, 0x33);
+}
+
+static u32 rv_divuw(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(1, rs2, rs1, 5, rd, 0x3b);
+}
+
+static u32 rv_divu(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(1, rs2, rs1, 5, rd, 0x33);
+}
+
+static u32 rv_remuw(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(1, rs2, rs1, 7, rd, 0x3b);
+}
+
+static u32 rv_remu(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(1, rs2, rs1, 7, rd, 0x33);
+}
+
+static u32 rv_sllw(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0, rs2, rs1, 1, rd, 0x3b);
+}
+
+static u32 rv_sll(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0, rs2, rs1, 1, rd, 0x33);
+}
+
+static u32 rv_srlw(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0, rs2, rs1, 5, rd, 0x3b);
+}
+
+static u32 rv_srl(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0, rs2, rs1, 5, rd, 0x33);
+}
+
+static u32 rv_sraw(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0x20, rs2, rs1, 5, rd, 0x3b);
+}
+
+static u32 rv_sra(u8 rd, u8 rs1, u8 rs2)
+{
+       return rv_r_insn(0x20, rs2, rs1, 5, rd, 0x33);
+}
+
+static u32 rv_lui(u8 rd, u32 imm31_12)
+{
+       return rv_u_insn(imm31_12, rd, 0x37);
+}
+
+static u32 rv_slli(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(imm11_0, rs1, 1, rd, 0x13);
+}
+
+static u32 rv_andi(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(imm11_0, rs1, 7, rd, 0x13);
+}
+
+static u32 rv_ori(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(imm11_0, rs1, 6, rd, 0x13);
+}
+
+static u32 rv_xori(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(imm11_0, rs1, 4, rd, 0x13);
+}
+
+static u32 rv_slliw(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(imm11_0, rs1, 1, rd, 0x1b);
+}
+
+static u32 rv_srliw(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(imm11_0, rs1, 5, rd, 0x1b);
+}
+
+static u32 rv_srli(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(imm11_0, rs1, 5, rd, 0x13);
+}
+
+static u32 rv_sraiw(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(0x400 | imm11_0, rs1, 5, rd, 0x1b);
+}
+
+static u32 rv_srai(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(0x400 | imm11_0, rs1, 5, rd, 0x13);
+}
+
+static u32 rv_jal(u8 rd, u32 imm20_1)
+{
+       return rv_uj_insn(imm20_1, rd, 0x6f);
+}
+
+static u32 rv_jalr(u8 rd, u8 rs1, u16 imm11_0)
+{
+       return rv_i_insn(imm11_0, rs1, 0, rd, 0x67);
+}
+
+static u32 rv_beq(u8 rs1, u8 rs2, u16 imm12_1)
+{
+       return rv_sb_insn(imm12_1, rs2, rs1, 0, 0x63);
+}
+
+static u32 rv_bltu(u8 rs1, u8 rs2, u16 imm12_1)
+{
+       return rv_sb_insn(imm12_1, rs2, rs1, 6, 0x63);
+}
+
+static u32 rv_bgeu(u8 rs1, u8 rs2, u16 imm12_1)
+{
+       return rv_sb_insn(imm12_1, rs2, rs1, 7, 0x63);
+}
+
+static u32 rv_bne(u8 rs1, u8 rs2, u16 imm12_1)
+{
+       return rv_sb_insn(imm12_1, rs2, rs1, 1, 0x63);
+}
+
+static u32 rv_blt(u8 rs1, u8 rs2, u16 imm12_1)
+{
+       return rv_sb_insn(imm12_1, rs2, rs1, 4, 0x63);
+}
+
+static u32 rv_bge(u8 rs1, u8 rs2, u16 imm12_1)
+{
+       return rv_sb_insn(imm12_1, rs2, rs1, 5, 0x63);
+}
+
+static u32 rv_sb(u8 rs1, u16 imm11_0, u8 rs2)
+{
+       return rv_s_insn(imm11_0, rs2, rs1, 0, 0x23);
+}
+
+static u32 rv_sh(u8 rs1, u16 imm11_0, u8 rs2)
+{
+       return rv_s_insn(imm11_0, rs2, rs1, 1, 0x23);
+}
+
+static u32 rv_sw(u8 rs1, u16 imm11_0, u8 rs2)
+{
+       return rv_s_insn(imm11_0, rs2, rs1, 2, 0x23);
+}
+
+static u32 rv_sd(u8 rs1, u16 imm11_0, u8 rs2)
+{
+       return rv_s_insn(imm11_0, rs2, rs1, 3, 0x23);
+}
+
+static u32 rv_lbu(u8 rd, u16 imm11_0, u8 rs1)
+{
+       return rv_i_insn(imm11_0, rs1, 4, rd, 0x03);
+}
+
+static u32 rv_lhu(u8 rd, u16 imm11_0, u8 rs1)
+{
+       return rv_i_insn(imm11_0, rs1, 5, rd, 0x03);
+}
+
+static u32 rv_lwu(u8 rd, u16 imm11_0, u8 rs1)
+{
+       return rv_i_insn(imm11_0, rs1, 6, rd, 0x03);
+}
+
+static u32 rv_ld(u8 rd, u16 imm11_0, u8 rs1)
+{
+       return rv_i_insn(imm11_0, rs1, 3, rd, 0x03);
+}
+
+static u32 rv_amoadd_w(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
+{
+       return rv_amo_insn(0, aq, rl, rs2, rs1, 2, rd, 0x2f);
+}
+
+static u32 rv_amoadd_d(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
+{
+       return rv_amo_insn(0, aq, rl, rs2, rs1, 3, rd, 0x2f);
+}
+
+static bool is_12b_int(s64 val)
+{
+       return -(1 << 11) <= val && val < (1 << 11);
+}
+
+static bool is_13b_int(s64 val)
+{
+       return -(1 << 12) <= val && val < (1 << 12);
+}
+
+static bool is_21b_int(s64 val)
+{
+       return -(1L << 20) <= val && val < (1L << 20);
+}
+
+static bool is_32b_int(s64 val)
+{
+       return -(1L << 31) <= val && val < (1L << 31);
+}
+
+static int is_12b_check(int off, int insn)
+{
+       if (!is_12b_int(off)) {
+               pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n",
+                      insn, (int)off);
+               return -1;
+       }
+       return 0;
+}
+
+static int is_13b_check(int off, int insn)
+{
+       if (!is_13b_int(off)) {
+               pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n",
+                      insn, (int)off);
+               return -1;
+       }
+       return 0;
+}
+
+static int is_21b_check(int off, int insn)
+{
+       if (!is_21b_int(off)) {
+               pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n",
+                      insn, (int)off);
+               return -1;
+       }
+       return 0;
+}
+
+static void emit_imm(u8 rd, s64 val, struct rv_jit_context *ctx)
+{
+       /* Note that the immediate from the add is sign-extended,
+        * which means that we need to compensate this by adding 2^12,
+        * when the 12th bit is set. A simpler way of doing this, and
+        * getting rid of the check, is to just add 2**11 before the
+        * shift. The "Loading a 32-Bit constant" example from the
+        * "Computer Organization and Design, RISC-V edition" book by
+        * Patterson/Hennessy highlights this fact.
+        *
+        * This also means that we need to process LSB to MSB.
+        */
+       s64 upper = (val + (1 << 11)) >> 12, lower = val & 0xfff;
+       int shift;
+
+       if (is_32b_int(val)) {
+               if (upper)
+                       emit(rv_lui(rd, upper), ctx);
+
+               if (!upper) {
+                       emit(rv_addi(rd, RV_REG_ZERO, lower), ctx);
+                       return;
+               }
+
+               emit(rv_addiw(rd, rd, lower), ctx);
+               return;
+       }
+
+       shift = __ffs(upper);
+       upper >>= shift;
+       shift += 12;
+
+       emit_imm(rd, upper, ctx);
+
+       emit(rv_slli(rd, rd, shift), ctx);
+       if (lower)
+               emit(rv_addi(rd, rd, lower), ctx);
+}
+
+static int rv_offset(int bpf_to, int bpf_from, struct rv_jit_context *ctx)
+{
+       int from = ctx->offset[bpf_from] - 1, to = ctx->offset[bpf_to];
+
+       return (to - from) << 2;
+}
+
+static int epilogue_offset(struct rv_jit_context *ctx)
+{
+       int to = ctx->epilogue_offset, from = ctx->ninsns;
+
+       return (to - from) << 2;
+}
+
+static void __build_epilogue(u8 reg, struct rv_jit_context *ctx)
+{
+       int stack_adjust = ctx->stack_size, store_offset = stack_adjust - 8;
+
+       if (seen_reg(RV_REG_RA, ctx)) {
+               emit(rv_ld(RV_REG_RA, store_offset, RV_REG_SP), ctx);
+               store_offset -= 8;
+       }
+       emit(rv_ld(RV_REG_FP, store_offset, RV_REG_SP), ctx);
+       store_offset -= 8;
+       if (seen_reg(RV_REG_S1, ctx)) {
+               emit(rv_ld(RV_REG_S1, store_offset, RV_REG_SP), ctx);
+               store_offset -= 8;
+       }
+       if (seen_reg(RV_REG_S2, ctx)) {
+               emit(rv_ld(RV_REG_S2, store_offset, RV_REG_SP), ctx);
+               store_offset -= 8;
+       }
+       if (seen_reg(RV_REG_S3, ctx)) {
+               emit(rv_ld(RV_REG_S3, store_offset, RV_REG_SP), ctx);
+               store_offset -= 8;
+       }
+       if (seen_reg(RV_REG_S4, ctx)) {
+               emit(rv_ld(RV_REG_S4, store_offset, RV_REG_SP), ctx);
+               store_offset -= 8;
+       }
+       if (seen_reg(RV_REG_S5, ctx)) {
+               emit(rv_ld(RV_REG_S5, store_offset, RV_REG_SP), ctx);
+               store_offset -= 8;
+       }
+       if (seen_reg(RV_REG_S6, ctx)) {
+               emit(rv_ld(RV_REG_S6, store_offset, RV_REG_SP), ctx);
+               store_offset -= 8;
+       }
+
+       emit(rv_addi(RV_REG_SP, RV_REG_SP, stack_adjust), ctx);
+       /* Set return value. */
+       emit(rv_addi(RV_REG_A0, RV_REG_A5, 0), ctx);
+       emit(rv_jalr(RV_REG_ZERO, reg, 0), ctx);
+}
+
+static void emit_zext_32(u8 reg, struct rv_jit_context *ctx)
+{
+       emit(rv_slli(reg, reg, 32), ctx);
+       emit(rv_srli(reg, reg, 32), ctx);
+}
+
+static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx)
+{
+       int tc_ninsn, off, start_insn = ctx->ninsns;
+       u8 tcc = rv_tail_call_reg(ctx);
+
+       /* a0: &ctx
+        * a1: &array
+        * a2: index
+        *
+        * if (index >= array->map.max_entries)
+        *      goto out;
+        */
+       tc_ninsn = insn ? ctx->offset[insn] - ctx->offset[insn - 1] :
+                  ctx->offset[0];
+       emit_zext_32(RV_REG_A2, ctx);
+
+       off = offsetof(struct bpf_array, map.max_entries);
+       if (is_12b_check(off, insn))
+               return -1;
+       emit(rv_lwu(RV_REG_T1, off, RV_REG_A1), ctx);
+       off = (tc_ninsn - (ctx->ninsns - start_insn)) << 2;
+       if (is_13b_check(off, insn))
+               return -1;
+       emit(rv_bgeu(RV_REG_A2, RV_REG_T1, off >> 1), ctx);
+
+       /* if (--TCC < 0)
+        *     goto out;
+        */
+       emit(rv_addi(RV_REG_T1, tcc, -1), ctx);
+       off = (tc_ninsn - (ctx->ninsns - start_insn)) << 2;
+       if (is_13b_check(off, insn))
+               return -1;
+       emit(rv_blt(RV_REG_T1, RV_REG_ZERO, off >> 1), ctx);
+
+       /* prog = array->ptrs[index];
+        * if (!prog)
+        *     goto out;
+        */
+       emit(rv_slli(RV_REG_T2, RV_REG_A2, 3), ctx);
+       emit(rv_add(RV_REG_T2, RV_REG_T2, RV_REG_A1), ctx);
+       off = offsetof(struct bpf_array, ptrs);
+       if (is_12b_check(off, insn))
+               return -1;
+       emit(rv_ld(RV_REG_T2, off, RV_REG_T2), ctx);
+       off = (tc_ninsn - (ctx->ninsns - start_insn)) << 2;
+       if (is_13b_check(off, insn))
+               return -1;
+       emit(rv_beq(RV_REG_T2, RV_REG_ZERO, off >> 1), ctx);
+
+       /* goto *(prog->bpf_func + 4); */
+       off = offsetof(struct bpf_prog, bpf_func);
+       if (is_12b_check(off, insn))
+               return -1;
+       emit(rv_ld(RV_REG_T3, off, RV_REG_T2), ctx);
+       emit(rv_addi(RV_REG_T3, RV_REG_T3, 4), ctx);
+       emit(rv_addi(RV_REG_TCC, RV_REG_T1, 0), ctx);
+       __build_epilogue(RV_REG_T3, ctx);
+       return 0;
+}
+
+static void init_regs(u8 *rd, u8 *rs, const struct bpf_insn *insn,
+                     struct rv_jit_context *ctx)
+{
+       u8 code = insn->code;
+
+       switch (code) {
+       case BPF_JMP | BPF_JA:
+       case BPF_JMP | BPF_CALL:
+       case BPF_JMP | BPF_EXIT:
+       case BPF_JMP | BPF_TAIL_CALL:
+               break;
+       default:
+               *rd = bpf_to_rv_reg(insn->dst_reg, ctx);
+       }
+
+       if (code & (BPF_ALU | BPF_X) || code & (BPF_ALU64 | BPF_X) ||
+           code & (BPF_JMP | BPF_X) || code & (BPF_JMP32 | BPF_X) ||
+           code & BPF_LDX || code & BPF_STX)
+               *rs = bpf_to_rv_reg(insn->src_reg, ctx);
+}
+
+static int rv_offset_check(int *rvoff, s16 off, int insn,
+                          struct rv_jit_context *ctx)
+{
+       *rvoff = rv_offset(insn + off, insn, ctx);
+       return is_13b_check(*rvoff, insn);
+}
+
+static void emit_zext_32_rd_rs(u8 *rd, u8 *rs, struct rv_jit_context *ctx)
+{
+       emit(rv_addi(RV_REG_T2, *rd, 0), ctx);
+       emit_zext_32(RV_REG_T2, ctx);
+       emit(rv_addi(RV_REG_T1, *rs, 0), ctx);
+       emit_zext_32(RV_REG_T1, ctx);
+       *rd = RV_REG_T2;
+       *rs = RV_REG_T1;
+}
+
+static void emit_sext_32_rd_rs(u8 *rd, u8 *rs, struct rv_jit_context *ctx)
+{
+       emit(rv_addiw(RV_REG_T2, *rd, 0), ctx);
+       emit(rv_addiw(RV_REG_T1, *rs, 0), ctx);
+       *rd = RV_REG_T2;
+       *rs = RV_REG_T1;
+}
+
+static void emit_zext_32_rd_t1(u8 *rd, struct rv_jit_context *ctx)
+{
+       emit(rv_addi(RV_REG_T2, *rd, 0), ctx);
+       emit_zext_32(RV_REG_T2, ctx);
+       emit_zext_32(RV_REG_T1, ctx);
+       *rd = RV_REG_T2;
+}
+
+static void emit_sext_32_rd(u8 *rd, struct rv_jit_context *ctx)
+{
+       emit(rv_addiw(RV_REG_T2, *rd, 0), ctx);
+       *rd = RV_REG_T2;
+}
+
+static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
+                    bool extra_pass)
+{
+       bool is64 = BPF_CLASS(insn->code) == BPF_ALU64 ||
+                   BPF_CLASS(insn->code) == BPF_JMP;
+       int rvoff, i = insn - ctx->prog->insnsi;
+       u8 rd = -1, rs = -1, code = insn->code;
+       s16 off = insn->off;
+       s32 imm = insn->imm;
+
+       init_regs(&rd, &rs, insn, ctx);
+
+       switch (code) {
+       /* dst = src */
+       case BPF_ALU | BPF_MOV | BPF_X:
+       case BPF_ALU64 | BPF_MOV | BPF_X:
+               emit(is64 ? rv_addi(rd, rs, 0) : rv_addiw(rd, rs, 0), ctx);
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+
+       /* dst = dst OP src */
+       case BPF_ALU | BPF_ADD | BPF_X:
+       case BPF_ALU64 | BPF_ADD | BPF_X:
+               emit(is64 ? rv_add(rd, rd, rs) : rv_addw(rd, rd, rs), ctx);
+               break;
+       case BPF_ALU | BPF_SUB | BPF_X:
+       case BPF_ALU64 | BPF_SUB | BPF_X:
+               emit(is64 ? rv_sub(rd, rd, rs) : rv_subw(rd, rd, rs), ctx);
+               break;
+       case BPF_ALU | BPF_AND | BPF_X:
+       case BPF_ALU64 | BPF_AND | BPF_X:
+               emit(rv_and(rd, rd, rs), ctx);
+               break;
+       case BPF_ALU | BPF_OR | BPF_X:
+       case BPF_ALU64 | BPF_OR | BPF_X:
+               emit(rv_or(rd, rd, rs), ctx);
+               break;
+       case BPF_ALU | BPF_XOR | BPF_X:
+       case BPF_ALU64 | BPF_XOR | BPF_X:
+               emit(rv_xor(rd, rd, rs), ctx);
+               break;
+       case BPF_ALU | BPF_MUL | BPF_X:
+       case BPF_ALU64 | BPF_MUL | BPF_X:
+               emit(is64 ? rv_mul(rd, rd, rs) : rv_mulw(rd, rd, rs), ctx);
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+       case BPF_ALU | BPF_DIV | BPF_X:
+       case BPF_ALU64 | BPF_DIV | BPF_X:
+               emit(is64 ? rv_divu(rd, rd, rs) : rv_divuw(rd, rd, rs), ctx);
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+       case BPF_ALU | BPF_MOD | BPF_X:
+       case BPF_ALU64 | BPF_MOD | BPF_X:
+               emit(is64 ? rv_remu(rd, rd, rs) : rv_remuw(rd, rd, rs), ctx);
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+       case BPF_ALU | BPF_LSH | BPF_X:
+       case BPF_ALU64 | BPF_LSH | BPF_X:
+               emit(is64 ? rv_sll(rd, rd, rs) : rv_sllw(rd, rd, rs), ctx);
+               break;
+       case BPF_ALU | BPF_RSH | BPF_X:
+       case BPF_ALU64 | BPF_RSH | BPF_X:
+               emit(is64 ? rv_srl(rd, rd, rs) : rv_srlw(rd, rd, rs), ctx);
+               break;
+       case BPF_ALU | BPF_ARSH | BPF_X:
+       case BPF_ALU64 | BPF_ARSH | BPF_X:
+               emit(is64 ? rv_sra(rd, rd, rs) : rv_sraw(rd, rd, rs), ctx);
+               break;
+
+       /* dst = -dst */
+       case BPF_ALU | BPF_NEG:
+       case BPF_ALU64 | BPF_NEG:
+               emit(is64 ? rv_sub(rd, RV_REG_ZERO, rd) :
+                    rv_subw(rd, RV_REG_ZERO, rd), ctx);
+               break;
+
+       /* dst = BSWAP##imm(dst) */
+       case BPF_ALU | BPF_END | BPF_FROM_LE:
+       {
+               int shift = 64 - imm;
+
+               emit(rv_slli(rd, rd, shift), ctx);
+               emit(rv_srli(rd, rd, shift), ctx);
+               break;
+       }
+       case BPF_ALU | BPF_END | BPF_FROM_BE:
+               emit(rv_addi(RV_REG_T2, RV_REG_ZERO, 0), ctx);
+
+               emit(rv_andi(RV_REG_T1, rd, 0xff), ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, RV_REG_T1), ctx);
+               emit(rv_slli(RV_REG_T2, RV_REG_T2, 8), ctx);
+               emit(rv_srli(rd, rd, 8), ctx);
+               if (imm == 16)
+                       goto out_be;
+
+               emit(rv_andi(RV_REG_T1, rd, 0xff), ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, RV_REG_T1), ctx);
+               emit(rv_slli(RV_REG_T2, RV_REG_T2, 8), ctx);
+               emit(rv_srli(rd, rd, 8), ctx);
+
+               emit(rv_andi(RV_REG_T1, rd, 0xff), ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, RV_REG_T1), ctx);
+               emit(rv_slli(RV_REG_T2, RV_REG_T2, 8), ctx);
+               emit(rv_srli(rd, rd, 8), ctx);
+               if (imm == 32)
+                       goto out_be;
+
+               emit(rv_andi(RV_REG_T1, rd, 0xff), ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, RV_REG_T1), ctx);
+               emit(rv_slli(RV_REG_T2, RV_REG_T2, 8), ctx);
+               emit(rv_srli(rd, rd, 8), ctx);
+
+               emit(rv_andi(RV_REG_T1, rd, 0xff), ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, RV_REG_T1), ctx);
+               emit(rv_slli(RV_REG_T2, RV_REG_T2, 8), ctx);
+               emit(rv_srli(rd, rd, 8), ctx);
+
+               emit(rv_andi(RV_REG_T1, rd, 0xff), ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, RV_REG_T1), ctx);
+               emit(rv_slli(RV_REG_T2, RV_REG_T2, 8), ctx);
+               emit(rv_srli(rd, rd, 8), ctx);
+
+               emit(rv_andi(RV_REG_T1, rd, 0xff), ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, RV_REG_T1), ctx);
+               emit(rv_slli(RV_REG_T2, RV_REG_T2, 8), ctx);
+               emit(rv_srli(rd, rd, 8), ctx);
+out_be:
+               emit(rv_andi(RV_REG_T1, rd, 0xff), ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, RV_REG_T1), ctx);
+
+               emit(rv_addi(rd, RV_REG_T2, 0), ctx);
+               break;
+
+       /* dst = imm */
+       case BPF_ALU | BPF_MOV | BPF_K:
+       case BPF_ALU64 | BPF_MOV | BPF_K:
+               emit_imm(rd, imm, ctx);
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+
+       /* dst = dst OP imm */
+       case BPF_ALU | BPF_ADD | BPF_K:
+       case BPF_ALU64 | BPF_ADD | BPF_K:
+               if (is_12b_int(imm)) {
+                       emit(is64 ? rv_addi(rd, rd, imm) :
+                            rv_addiw(rd, rd, imm), ctx);
+               } else {
+                       emit_imm(RV_REG_T1, imm, ctx);
+                       emit(is64 ? rv_add(rd, rd, RV_REG_T1) :
+                            rv_addw(rd, rd, RV_REG_T1), ctx);
+               }
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+       case BPF_ALU | BPF_SUB | BPF_K:
+       case BPF_ALU64 | BPF_SUB | BPF_K:
+               if (is_12b_int(-imm)) {
+                       emit(is64 ? rv_addi(rd, rd, -imm) :
+                            rv_addiw(rd, rd, -imm), ctx);
+               } else {
+                       emit_imm(RV_REG_T1, imm, ctx);
+                       emit(is64 ? rv_sub(rd, rd, RV_REG_T1) :
+                            rv_subw(rd, rd, RV_REG_T1), ctx);
+               }
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+       case BPF_ALU | BPF_AND | BPF_K:
+       case BPF_ALU64 | BPF_AND | BPF_K:
+               if (is_12b_int(imm)) {
+                       emit(rv_andi(rd, rd, imm), ctx);
+               } else {
+                       emit_imm(RV_REG_T1, imm, ctx);
+                       emit(rv_and(rd, rd, RV_REG_T1), ctx);
+               }
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+       case BPF_ALU | BPF_OR | BPF_K:
+       case BPF_ALU64 | BPF_OR | BPF_K:
+               if (is_12b_int(imm)) {
+                       emit(rv_ori(rd, rd, imm), ctx);
+               } else {
+                       emit_imm(RV_REG_T1, imm, ctx);
+                       emit(rv_or(rd, rd, RV_REG_T1), ctx);
+               }
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+       case BPF_ALU | BPF_XOR | BPF_K:
+       case BPF_ALU64 | BPF_XOR | BPF_K:
+               if (is_12b_int(imm)) {
+                       emit(rv_xori(rd, rd, imm), ctx);
+               } else {
+                       emit_imm(RV_REG_T1, imm, ctx);
+                       emit(rv_xor(rd, rd, RV_REG_T1), ctx);
+               }
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+       case BPF_ALU | BPF_MUL | BPF_K:
+       case BPF_ALU64 | BPF_MUL | BPF_K:
+               emit_imm(RV_REG_T1, imm, ctx);
+               emit(is64 ? rv_mul(rd, rd, RV_REG_T1) :
+                    rv_mulw(rd, rd, RV_REG_T1), ctx);
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+       case BPF_ALU | BPF_DIV | BPF_K:
+       case BPF_ALU64 | BPF_DIV | BPF_K:
+               emit_imm(RV_REG_T1, imm, ctx);
+               emit(is64 ? rv_divu(rd, rd, RV_REG_T1) :
+                    rv_divuw(rd, rd, RV_REG_T1), ctx);
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+       case BPF_ALU | BPF_MOD | BPF_K:
+       case BPF_ALU64 | BPF_MOD | BPF_K:
+               emit_imm(RV_REG_T1, imm, ctx);
+               emit(is64 ? rv_remu(rd, rd, RV_REG_T1) :
+                    rv_remuw(rd, rd, RV_REG_T1), ctx);
+               if (!is64)
+                       emit_zext_32(rd, ctx);
+               break;
+       case BPF_ALU | BPF_LSH | BPF_K:
+       case BPF_ALU64 | BPF_LSH | BPF_K:
+               emit(is64 ? rv_slli(rd, rd, imm) : rv_slliw(rd, rd, imm), ctx);
+               break;
+       case BPF_ALU | BPF_RSH | BPF_K:
+       case BPF_ALU64 | BPF_RSH | BPF_K:
+               emit(is64 ? rv_srli(rd, rd, imm) : rv_srliw(rd, rd, imm), ctx);
+               break;
+       case BPF_ALU | BPF_ARSH | BPF_K:
+       case BPF_ALU64 | BPF_ARSH | BPF_K:
+               emit(is64 ? rv_srai(rd, rd, imm) : rv_sraiw(rd, rd, imm), ctx);
+               break;
+
+       /* JUMP off */
+       case BPF_JMP | BPF_JA:
+               rvoff = rv_offset(i + off, i, ctx);
+               if (!is_21b_int(rvoff)) {
+                       pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n",
+                              i, rvoff);
+                       return -1;
+               }
+
+               emit(rv_jal(RV_REG_ZERO, rvoff >> 1), ctx);
+               break;
+
+       /* IF (dst COND src) JUMP off */
+       case BPF_JMP | BPF_JEQ | BPF_X:
+       case BPF_JMP32 | BPF_JEQ | BPF_X:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               if (!is64)
+                       emit_zext_32_rd_rs(&rd, &rs, ctx);
+               emit(rv_beq(rd, rs, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JGT | BPF_X:
+       case BPF_JMP32 | BPF_JGT | BPF_X:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               if (!is64)
+                       emit_zext_32_rd_rs(&rd, &rs, ctx);
+               emit(rv_bltu(rs, rd, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JLT | BPF_X:
+       case BPF_JMP32 | BPF_JLT | BPF_X:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               if (!is64)
+                       emit_zext_32_rd_rs(&rd, &rs, ctx);
+               emit(rv_bltu(rd, rs, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JGE | BPF_X:
+       case BPF_JMP32 | BPF_JGE | BPF_X:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               if (!is64)
+                       emit_zext_32_rd_rs(&rd, &rs, ctx);
+               emit(rv_bgeu(rd, rs, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JLE | BPF_X:
+       case BPF_JMP32 | BPF_JLE | BPF_X:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               if (!is64)
+                       emit_zext_32_rd_rs(&rd, &rs, ctx);
+               emit(rv_bgeu(rs, rd, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JNE | BPF_X:
+       case BPF_JMP32 | BPF_JNE | BPF_X:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               if (!is64)
+                       emit_zext_32_rd_rs(&rd, &rs, ctx);
+               emit(rv_bne(rd, rs, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JSGT | BPF_X:
+       case BPF_JMP32 | BPF_JSGT | BPF_X:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               if (!is64)
+                       emit_sext_32_rd_rs(&rd, &rs, ctx);
+               emit(rv_blt(rs, rd, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JSLT | BPF_X:
+       case BPF_JMP32 | BPF_JSLT | BPF_X:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               if (!is64)
+                       emit_sext_32_rd_rs(&rd, &rs, ctx);
+               emit(rv_blt(rd, rs, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JSGE | BPF_X:
+       case BPF_JMP32 | BPF_JSGE | BPF_X:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               if (!is64)
+                       emit_sext_32_rd_rs(&rd, &rs, ctx);
+               emit(rv_bge(rd, rs, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JSLE | BPF_X:
+       case BPF_JMP32 | BPF_JSLE | BPF_X:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               if (!is64)
+                       emit_sext_32_rd_rs(&rd, &rs, ctx);
+               emit(rv_bge(rs, rd, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JSET | BPF_X:
+       case BPF_JMP32 | BPF_JSET | BPF_X:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               if (!is64)
+                       emit_zext_32_rd_rs(&rd, &rs, ctx);
+               emit(rv_and(RV_REG_T1, rd, rs), ctx);
+               emit(rv_bne(RV_REG_T1, RV_REG_ZERO, rvoff >> 1), ctx);
+               break;
+
+       /* IF (dst COND imm) JUMP off */
+       case BPF_JMP | BPF_JEQ | BPF_K:
+       case BPF_JMP32 | BPF_JEQ | BPF_K:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (!is64)
+                       emit_zext_32_rd_t1(&rd, ctx);
+               emit(rv_beq(rd, RV_REG_T1, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JGT | BPF_K:
+       case BPF_JMP32 | BPF_JGT | BPF_K:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (!is64)
+                       emit_zext_32_rd_t1(&rd, ctx);
+               emit(rv_bltu(RV_REG_T1, rd, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JLT | BPF_K:
+       case BPF_JMP32 | BPF_JLT | BPF_K:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (!is64)
+                       emit_zext_32_rd_t1(&rd, ctx);
+               emit(rv_bltu(rd, RV_REG_T1, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JGE | BPF_K:
+       case BPF_JMP32 | BPF_JGE | BPF_K:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (!is64)
+                       emit_zext_32_rd_t1(&rd, ctx);
+               emit(rv_bgeu(rd, RV_REG_T1, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JLE | BPF_K:
+       case BPF_JMP32 | BPF_JLE | BPF_K:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (!is64)
+                       emit_zext_32_rd_t1(&rd, ctx);
+               emit(rv_bgeu(RV_REG_T1, rd, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JNE | BPF_K:
+       case BPF_JMP32 | BPF_JNE | BPF_K:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (!is64)
+                       emit_zext_32_rd_t1(&rd, ctx);
+               emit(rv_bne(rd, RV_REG_T1, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JSGT | BPF_K:
+       case BPF_JMP32 | BPF_JSGT | BPF_K:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (!is64)
+                       emit_sext_32_rd(&rd, ctx);
+               emit(rv_blt(RV_REG_T1, rd, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JSLT | BPF_K:
+       case BPF_JMP32 | BPF_JSLT | BPF_K:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (!is64)
+                       emit_sext_32_rd(&rd, ctx);
+               emit(rv_blt(rd, RV_REG_T1, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JSGE | BPF_K:
+       case BPF_JMP32 | BPF_JSGE | BPF_K:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (!is64)
+                       emit_sext_32_rd(&rd, ctx);
+               emit(rv_bge(rd, RV_REG_T1, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JSLE | BPF_K:
+       case BPF_JMP32 | BPF_JSLE | BPF_K:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (!is64)
+                       emit_sext_32_rd(&rd, ctx);
+               emit(rv_bge(RV_REG_T1, rd, rvoff >> 1), ctx);
+               break;
+       case BPF_JMP | BPF_JSET | BPF_K:
+       case BPF_JMP32 | BPF_JSET | BPF_K:
+               if (rv_offset_check(&rvoff, off, i, ctx))
+                       return -1;
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (!is64)
+                       emit_zext_32_rd_t1(&rd, ctx);
+               emit(rv_and(RV_REG_T1, rd, RV_REG_T1), ctx);
+               emit(rv_bne(RV_REG_T1, RV_REG_ZERO, rvoff >> 1), ctx);
+               break;
+
+       /* function call */
+       case BPF_JMP | BPF_CALL:
+       {
+               bool fixed;
+               int i, ret;
+               u64 addr;
+
+               mark_call(ctx);
+               ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, &addr,
+                                           &fixed);
+               if (ret < 0)
+                       return ret;
+               if (fixed) {
+                       emit_imm(RV_REG_T1, addr, ctx);
+               } else {
+                       i = ctx->ninsns;
+                       emit_imm(RV_REG_T1, addr, ctx);
+                       for (i = ctx->ninsns - i; i < 8; i++) {
+                               /* nop */
+                               emit(rv_addi(RV_REG_ZERO, RV_REG_ZERO, 0),
+                                    ctx);
+                       }
+               }
+               emit(rv_jalr(RV_REG_RA, RV_REG_T1, 0), ctx);
+               rd = bpf_to_rv_reg(BPF_REG_0, ctx);
+               emit(rv_addi(rd, RV_REG_A0, 0), ctx);
+               break;
+       }
+       /* tail call */
+       case BPF_JMP | BPF_TAIL_CALL:
+               if (emit_bpf_tail_call(i, ctx))
+                       return -1;
+               break;
+
+       /* function return */
+       case BPF_JMP | BPF_EXIT:
+               if (i == ctx->prog->len - 1)
+                       break;
+
+               rvoff = epilogue_offset(ctx);
+               if (is_21b_check(rvoff, i))
+                       return -1;
+               emit(rv_jal(RV_REG_ZERO, rvoff >> 1), ctx);
+               break;
+
+       /* dst = imm64 */
+       case BPF_LD | BPF_IMM | BPF_DW:
+       {
+               struct bpf_insn insn1 = insn[1];
+               u64 imm64;
+
+               imm64 = (u64)insn1.imm << 32 | (u32)imm;
+               emit_imm(rd, imm64, ctx);
+               return 1;
+       }
+
+       /* LDX: dst = *(size *)(src + off) */
+       case BPF_LDX | BPF_MEM | BPF_B:
+               if (is_12b_int(off)) {
+                       emit(rv_lbu(rd, off, rs), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T1, off, ctx);
+               emit(rv_add(RV_REG_T1, RV_REG_T1, rs), ctx);
+               emit(rv_lbu(rd, 0, RV_REG_T1), ctx);
+               break;
+       case BPF_LDX | BPF_MEM | BPF_H:
+               if (is_12b_int(off)) {
+                       emit(rv_lhu(rd, off, rs), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T1, off, ctx);
+               emit(rv_add(RV_REG_T1, RV_REG_T1, rs), ctx);
+               emit(rv_lhu(rd, 0, RV_REG_T1), ctx);
+               break;
+       case BPF_LDX | BPF_MEM | BPF_W:
+               if (is_12b_int(off)) {
+                       emit(rv_lwu(rd, off, rs), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T1, off, ctx);
+               emit(rv_add(RV_REG_T1, RV_REG_T1, rs), ctx);
+               emit(rv_lwu(rd, 0, RV_REG_T1), ctx);
+               break;
+       case BPF_LDX | BPF_MEM | BPF_DW:
+               if (is_12b_int(off)) {
+                       emit(rv_ld(rd, off, rs), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T1, off, ctx);
+               emit(rv_add(RV_REG_T1, RV_REG_T1, rs), ctx);
+               emit(rv_ld(rd, 0, RV_REG_T1), ctx);
+               break;
+
+       /* ST: *(size *)(dst + off) = imm */
+       case BPF_ST | BPF_MEM | BPF_B:
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (is_12b_int(off)) {
+                       emit(rv_sb(rd, off, RV_REG_T1), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T2, off, ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, rd), ctx);
+               emit(rv_sb(RV_REG_T2, 0, RV_REG_T1), ctx);
+               break;
+
+       case BPF_ST | BPF_MEM | BPF_H:
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (is_12b_int(off)) {
+                       emit(rv_sh(rd, off, RV_REG_T1), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T2, off, ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, rd), ctx);
+               emit(rv_sh(RV_REG_T2, 0, RV_REG_T1), ctx);
+               break;
+       case BPF_ST | BPF_MEM | BPF_W:
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (is_12b_int(off)) {
+                       emit(rv_sw(rd, off, RV_REG_T1), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T2, off, ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, rd), ctx);
+               emit(rv_sw(RV_REG_T2, 0, RV_REG_T1), ctx);
+               break;
+       case BPF_ST | BPF_MEM | BPF_DW:
+               emit_imm(RV_REG_T1, imm, ctx);
+               if (is_12b_int(off)) {
+                       emit(rv_sd(rd, off, RV_REG_T1), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T2, off, ctx);
+               emit(rv_add(RV_REG_T2, RV_REG_T2, rd), ctx);
+               emit(rv_sd(RV_REG_T2, 0, RV_REG_T1), ctx);
+               break;
+
+       /* STX: *(size *)(dst + off) = src */
+       case BPF_STX | BPF_MEM | BPF_B:
+               if (is_12b_int(off)) {
+                       emit(rv_sb(rd, off, rs), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T1, off, ctx);
+               emit(rv_add(RV_REG_T1, RV_REG_T1, rd), ctx);
+               emit(rv_sb(RV_REG_T1, 0, rs), ctx);
+               break;
+       case BPF_STX | BPF_MEM | BPF_H:
+               if (is_12b_int(off)) {
+                       emit(rv_sh(rd, off, rs), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T1, off, ctx);
+               emit(rv_add(RV_REG_T1, RV_REG_T1, rd), ctx);
+               emit(rv_sh(RV_REG_T1, 0, rs), ctx);
+               break;
+       case BPF_STX | BPF_MEM | BPF_W:
+               if (is_12b_int(off)) {
+                       emit(rv_sw(rd, off, rs), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T1, off, ctx);
+               emit(rv_add(RV_REG_T1, RV_REG_T1, rd), ctx);
+               emit(rv_sw(RV_REG_T1, 0, rs), ctx);
+               break;
+       case BPF_STX | BPF_MEM | BPF_DW:
+               if (is_12b_int(off)) {
+                       emit(rv_sd(rd, off, rs), ctx);
+                       break;
+               }
+
+               emit_imm(RV_REG_T1, off, ctx);
+               emit(rv_add(RV_REG_T1, RV_REG_T1, rd), ctx);
+               emit(rv_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:
+               if (off) {
+                       if (is_12b_int(off)) {
+                               emit(rv_addi(RV_REG_T1, rd, off), ctx);
+                       } else {
+                               emit_imm(RV_REG_T1, off, ctx);
+                               emit(rv_add(RV_REG_T1, RV_REG_T1, rd), ctx);
+                       }
+
+                       rd = RV_REG_T1;
+               }
+
+               emit(BPF_SIZE(code) == BPF_W ?
+                    rv_amoadd_w(RV_REG_ZERO, rs, rd, 0, 0) :
+                    rv_amoadd_d(RV_REG_ZERO, rs, rd, 0, 0), ctx);
+               break;
+       default:
+               pr_err("bpf-jit: unknown opcode %02x\n", code);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void build_prologue(struct rv_jit_context *ctx)
+{
+       int stack_adjust = 0, store_offset, bpf_stack_adjust;
+
+       if (seen_reg(RV_REG_RA, ctx))
+               stack_adjust += 8;
+       stack_adjust += 8; /* RV_REG_FP */
+       if (seen_reg(RV_REG_S1, ctx))
+               stack_adjust += 8;
+       if (seen_reg(RV_REG_S2, ctx))
+               stack_adjust += 8;
+       if (seen_reg(RV_REG_S3, ctx))
+               stack_adjust += 8;
+       if (seen_reg(RV_REG_S4, ctx))
+               stack_adjust += 8;
+       if (seen_reg(RV_REG_S5, ctx))
+               stack_adjust += 8;
+       if (seen_reg(RV_REG_S6, ctx))
+               stack_adjust += 8;
+
+       stack_adjust = round_up(stack_adjust, 16);
+       bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16);
+       stack_adjust += bpf_stack_adjust;
+
+       store_offset = stack_adjust - 8;
+
+       /* First instruction is always setting the tail-call-counter
+        * (TCC) register. This instruction is skipped for tail calls.
+        */
+       emit(rv_addi(RV_REG_TCC, RV_REG_ZERO, MAX_TAIL_CALL_CNT), ctx);
+
+       emit(rv_addi(RV_REG_SP, RV_REG_SP, -stack_adjust), ctx);
+
+       if (seen_reg(RV_REG_RA, ctx)) {
+               emit(rv_sd(RV_REG_SP, store_offset, RV_REG_RA), ctx);
+               store_offset -= 8;
+       }
+       emit(rv_sd(RV_REG_SP, store_offset, RV_REG_FP), ctx);
+       store_offset -= 8;
+       if (seen_reg(RV_REG_S1, ctx)) {
+               emit(rv_sd(RV_REG_SP, store_offset, RV_REG_S1), ctx);
+               store_offset -= 8;
+       }
+       if (seen_reg(RV_REG_S2, ctx)) {
+               emit(rv_sd(RV_REG_SP, store_offset, RV_REG_S2), ctx);
+               store_offset -= 8;
+       }
+       if (seen_reg(RV_REG_S3, ctx)) {
+               emit(rv_sd(RV_REG_SP, store_offset, RV_REG_S3), ctx);
+               store_offset -= 8;
+       }
+       if (seen_reg(RV_REG_S4, ctx)) {
+               emit(rv_sd(RV_REG_SP, store_offset, RV_REG_S4), ctx);
+               store_offset -= 8;
+       }
+       if (seen_reg(RV_REG_S5, ctx)) {
+               emit(rv_sd(RV_REG_SP, store_offset, RV_REG_S5), ctx);
+               store_offset -= 8;
+       }
+       if (seen_reg(RV_REG_S6, ctx)) {
+               emit(rv_sd(RV_REG_SP, store_offset, RV_REG_S6), ctx);
+               store_offset -= 8;
+       }
+
+       emit(rv_addi(RV_REG_FP, RV_REG_SP, stack_adjust), ctx);
+
+       if (bpf_stack_adjust)
+               emit(rv_addi(RV_REG_S5, RV_REG_SP, bpf_stack_adjust), ctx);
+
+       /* Program contains calls and tail calls, so RV_REG_TCC need
+        * to be saved across calls.
+        */
+       if (seen_tail_call(ctx) && seen_call(ctx))
+               emit(rv_addi(RV_REG_TCC_SAVED, RV_REG_TCC, 0), ctx);
+
+       ctx->stack_size = stack_adjust;
+}
+
+static void build_epilogue(struct rv_jit_context *ctx)
+{
+       __build_epilogue(RV_REG_RA, ctx);
+}
+
+static int build_body(struct rv_jit_context *ctx, bool extra_pass)
+{
+       const struct bpf_prog *prog = ctx->prog;
+       int i;
+
+       for (i = 0; i < prog->len; i++) {
+               const struct bpf_insn *insn = &prog->insnsi[i];
+               int ret;
+
+               ret = emit_insn(insn, ctx, extra_pass);
+               if (ret > 0) {
+                       i++;
+                       if (ctx->insns == NULL)
+                               ctx->offset[i] = ctx->ninsns;
+                       continue;
+               }
+               if (ctx->insns == NULL)
+                       ctx->offset[i] = ctx->ninsns;
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+static void bpf_fill_ill_insns(void *area, unsigned int size)
+{
+       memset(area, 0, size);
+}
+
+static void bpf_flush_icache(void *start, void *end)
+{
+       flush_icache_range((unsigned long)start, (unsigned long)end);
+}
+
+struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+{
+       bool tmp_blinded = false, extra_pass = false;
+       struct bpf_prog *tmp, *orig_prog = prog;
+       struct rv_jit_data *jit_data;
+       struct rv_jit_context *ctx;
+       unsigned int image_size;
+
+       if (!prog->jit_requested)
+               return orig_prog;
+
+       tmp = bpf_jit_blind_constants(prog);
+       if (IS_ERR(tmp))
+               return orig_prog;
+       if (tmp != prog) {
+               tmp_blinded = true;
+               prog = tmp;
+       }
+
+       jit_data = prog->aux->jit_data;
+       if (!jit_data) {
+               jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL);
+               if (!jit_data) {
+                       prog = orig_prog;
+                       goto out;
+               }
+               prog->aux->jit_data = jit_data;
+       }
+
+       ctx = &jit_data->ctx;
+
+       if (ctx->offset) {
+               extra_pass = true;
+               image_size = sizeof(u32) * ctx->ninsns;
+               goto skip_init_ctx;
+       }
+
+       ctx->prog = prog;
+       ctx->offset = kcalloc(prog->len, sizeof(int), GFP_KERNEL);
+       if (!ctx->offset) {
+               prog = orig_prog;
+               goto out_offset;
+       }
+
+       /* First pass generates the ctx->offset, but does not emit an image. */
+       if (build_body(ctx, extra_pass)) {
+               prog = orig_prog;
+               goto out_offset;
+       }
+       build_prologue(ctx);
+       ctx->epilogue_offset = ctx->ninsns;
+       build_epilogue(ctx);
+
+       /* Allocate image, now that we know the size. */
+       image_size = sizeof(u32) * ctx->ninsns;
+       jit_data->header = bpf_jit_binary_alloc(image_size, &jit_data->image,
+                                               sizeof(u32),
+                                               bpf_fill_ill_insns);
+       if (!jit_data->header) {
+               prog = orig_prog;
+               goto out_offset;
+       }
+
+       /* Second, real pass, that acutally emits the image. */
+       ctx->insns = (u32 *)jit_data->image;
+skip_init_ctx:
+       ctx->ninsns = 0;
+
+       build_prologue(ctx);
+       if (build_body(ctx, extra_pass)) {
+               bpf_jit_binary_free(jit_data->header);
+               prog = orig_prog;
+               goto out_offset;
+       }
+       build_epilogue(ctx);
+
+       if (bpf_jit_enable > 1)
+               bpf_jit_dump(prog->len, image_size, 2, ctx->insns);
+
+       prog->bpf_func = (void *)ctx->insns;
+       prog->jited = 1;
+       prog->jited_len = image_size;
+
+       bpf_flush_icache(jit_data->header, ctx->insns + ctx->ninsns);
+
+       if (!prog->is_func || extra_pass) {
+out_offset:
+               kfree(ctx->offset);
+               kfree(jit_data);
+               prog->aux->jit_data = NULL;
+       }
+out:
+       if (tmp_blinded)
+               bpf_jit_prog_release_other(prog, prog == orig_prog ?
+                                          tmp : orig_prog);
+       return prog;
+}
index 6e27858..5739276 100644 (file)
 #include <linux/device.h>
 #include <linux/types.h>
 
-#define PNETIDS_LEN            64      /* Total utility string length in bytes
-                                        * to cover up to 4 PNETIDs of 16 bytes
-                                        * for up to 4 device ports
-                                        */
-#define MAX_PNETID_LEN         16      /* Max.length of a single port PNETID */
-#define MAX_PNETID_PORTS       (PNETIDS_LEN / MAX_PNETID_LEN)
-                                       /* Max. # of ports with a PNETID */
-
 int pnet_id_by_dev_port(struct device *dev, unsigned short port, u8 *pnetid);
 #endif /* _ASM_S390_PNET_H */
index da3e0d4..6b0f30b 100644 (file)
@@ -3,3 +3,4 @@ include include/uapi/asm-generic/Kbuild.asm
 
 generated-y += unistd_32.h
 generated-y += unistd_64.h
+generic-y += socket.h
diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h
deleted file mode 100644 (file)
index 49c9715..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*
- *  S390 version
- *
- *  Derived from "include/asm-i386/socket.h"
- */
-
-#ifndef _ASM_SOCKET_H
-#define _ASM_SOCKET_H
-
-#include <asm/sockios.h>
-
-/* For setsockopt(2) */
-#define SOL_SOCKET     1
-
-#define SO_DEBUG       1
-#define SO_REUSEADDR   2
-#define SO_TYPE                3
-#define SO_ERROR       4
-#define SO_DONTROUTE   5
-#define SO_BROADCAST   6
-#define SO_SNDBUF      7
-#define SO_RCVBUF      8
-#define SO_SNDBUFFORCE 32
-#define SO_RCVBUFFORCE 33
-#define SO_KEEPALIVE   9
-#define SO_OOBINLINE   10
-#define SO_NO_CHECK    11
-#define SO_PRIORITY    12
-#define SO_LINGER      13
-#define SO_BSDCOMPAT   14
-#define SO_REUSEPORT   15
-#define SO_PASSCRED    16
-#define SO_PEERCRED    17
-#define SO_RCVLOWAT    18
-#define SO_SNDLOWAT    19
-#define SO_RCVTIMEO    20
-#define SO_SNDTIMEO    21
-
-/* Security levels - as per NRL IPv6 - don't actually do anything */
-#define SO_SECURITY_AUTHENTICATION             22
-#define SO_SECURITY_ENCRYPTION_TRANSPORT       23
-#define SO_SECURITY_ENCRYPTION_NETWORK         24
-
-#define SO_BINDTODEVICE        25
-
-/* Socket filtering */
-#define SO_ATTACH_FILTER        26
-#define SO_DETACH_FILTER        27
-#define SO_GET_FILTER          SO_ATTACH_FILTER
-
-#define SO_PEERNAME            28
-#define SO_TIMESTAMP           29
-#define SCM_TIMESTAMP          SO_TIMESTAMP
-
-#define SO_ACCEPTCONN          30
-
-#define SO_PEERSEC             31
-#define SO_PASSSEC             34
-#define SO_TIMESTAMPNS         35
-#define SCM_TIMESTAMPNS                SO_TIMESTAMPNS
-
-#define SO_MARK                        36
-
-#define SO_TIMESTAMPING                37
-#define SCM_TIMESTAMPING       SO_TIMESTAMPING
-
-#define SO_PROTOCOL            38
-#define SO_DOMAIN              39
-
-#define SO_RXQ_OVFL             40
-
-#define SO_WIFI_STATUS         41
-#define SCM_WIFI_STATUS                SO_WIFI_STATUS
-#define SO_PEEK_OFF            42
-
-/* Instruct lower device to use last 4-bytes of skb data as FCS */
-#define SO_NOFCS               43
-
-#define SO_LOCK_FILTER         44
-
-#define SO_SELECT_ERR_QUEUE    45
-
-#define SO_BUSY_POLL           46
-
-#define SO_MAX_PACING_RATE     47
-
-#define SO_BPF_EXTENSIONS      48
-
-#define SO_INCOMING_CPU                49
-
-#define SO_ATTACH_BPF          50
-#define SO_DETACH_BPF          SO_DETACH_FILTER
-
-#define SO_ATTACH_REUSEPORT_CBPF       51
-#define SO_ATTACH_REUSEPORT_EBPF       52
-
-#define SO_CNX_ADVICE          53
-
-#define SCM_TIMESTAMPING_OPT_STATS     54
-
-#define        SO_MEMINFO              55
-
-#define SO_INCOMING_NAPI_ID    56
-
-#define SO_COOKIE              57
-
-#define SCM_TIMESTAMPING_PKTINFO       58
-
-#define SO_PEERGROUPS          59
-
-#define SO_ZEROCOPY            60
-
-#define SO_TXTIME              61
-#define SCM_TXTIME             SO_TXTIME
-
-#define SO_BINDTOIFINDEX       62
-
-#endif /* _ASM_SOCKET_H */
index 537f97f..b6796e6 100644 (file)
        .section .text
 ENTRY(swsusp_arch_suspend)
        lg      %r1,__LC_NODAT_STACK
-       aghi    %r1,-STACK_FRAME_OVERHEAD
        stmg    %r6,%r15,__SF_GPRS(%r1)
+       aghi    %r1,-STACK_FRAME_OVERHEAD
        stg     %r15,__SF_BACKCHAIN(%r1)
-       lgr     %r1,%r15
+       lgr     %r15,%r1
 
        /* Store FPU registers */
        brasl   %r14,save_fpu_regs
index ce9defd..51dd026 100644 (file)
@@ -1154,7 +1154,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i
                mask = 0x7000; /* jnz */
                if (BPF_CLASS(insn->code) == BPF_JMP32) {
                        /* llilf %w1,imm (load zero extend imm) */
-                       EMIT6_IMM(0xc0010000, REG_W1, imm);
+                       EMIT6_IMM(0xc00f0000, REG_W1, imm);
                        /* nr %w1,%dst */
                        EMIT2(0x1400, REG_W1, dst_reg);
                } else {
@@ -1216,6 +1216,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i
                          REG_W1, dst_reg, src_reg);
                goto branch_oc;
 branch_ks:
+               is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
                /* lgfi %w1,imm (load sign extend imm) */
                EMIT6_IMM(0xc0010000, REG_W1, imm);
                /* crj or cgrj %dst,%w1,mask,off */
@@ -1223,6 +1224,7 @@ branch_ks:
                            dst_reg, REG_W1, i, off, mask);
                break;
 branch_ku:
+               is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
                /* lgfi %w1,imm (load sign extend imm) */
                EMIT6_IMM(0xc0010000, REG_W1, imm);
                /* clrj or clgrj %dst,%w1,mask,off */
@@ -1230,11 +1232,13 @@ branch_ku:
                            dst_reg, REG_W1, i, off, mask);
                break;
 branch_xs:
+               is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
                /* crj or cgrj %dst,%src,mask,off */
                EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0076 : 0x0064),
                            dst_reg, src_reg, i, off, mask);
                break;
 branch_xu:
+               is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
                /* clrj or clgrj %dst,%src,mask,off */
                EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0077 : 0x0065),
                            dst_reg, src_reg, i, off, mask);
index e22f1b1..9ecdbdf 100644 (file)
 #include <asm/ccwdev.h>
 #include <asm/pnet.h>
 
+#define PNETIDS_LEN            64      /* Total utility string length in bytes
+                                        * to cover up to 4 PNETIDs of 16 bytes
+                                        * for up to 4 device ports
+                                        */
+#define MAX_PNETID_LEN         16      /* Max.length of a single port PNETID */
+#define MAX_PNETID_PORTS       (PNETIDS_LEN / MAX_PNETID_LEN)
+                                       /* Max. # of ports with a PNETID */
+
 /*
  * Get the PNETIDs from a device.
  * s390 hardware supports the definition of a so-called Physical Network
index a966d7b..4266a4d 100644 (file)
@@ -382,7 +382,9 @@ static void zpci_irq_handler(struct airq_struct *airq)
                        if (ai == -1UL)
                                break;
                        inc_irq_stat(IRQIO_MSI);
+                       airq_iv_lock(aibv, ai);
                        generic_handle_irq(airq_iv_get_data(aibv, ai));
+                       airq_iv_unlock(aibv, ai);
                }
        }
 }
@@ -408,7 +410,7 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
        zdev->aisb = aisb;
 
        /* Create adapter interrupt vector */
-       zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA);
+       zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | AIRQ_IV_BITLOCK);
        if (!zdev->aibv)
                return -ENOMEM;
 
index fec499d..f139e00 100644 (file)
@@ -19,6 +19,16 @@ typedef unsigned short         __kernel_old_gid_t;
 typedef int                   __kernel_suseconds_t;
 #define __kernel_suseconds_t __kernel_suseconds_t
 
+typedef long           __kernel_long_t;
+typedef unsigned long  __kernel_ulong_t;
+#define __kernel_long_t __kernel_long_t
+
+struct __kernel_old_timeval {
+       __kernel_long_t tv_sec;
+       __kernel_suseconds_t tv_usec;
+};
+#define __kernel_old_timeval __kernel_old_timeval
+
 #else
 /* sparc 32 bit */
 
index bbdb815..88fe4f9 100644 (file)
@@ -3,6 +3,7 @@
 #define _ASM_SOCKET_H
 
 #include <asm/sockios.h>
+#include <asm/bitsperlong.h>
 
 /* For setsockopt(2) */
 #define SOL_SOCKET     0xffff
@@ -20,8 +21,8 @@
 #define SO_BSDCOMPAT    0x0400
 #define SO_RCVLOWAT     0x0800
 #define SO_SNDLOWAT     0x1000
-#define SO_RCVTIMEO     0x2000
-#define SO_SNDTIMEO     0x4000
+#define SO_RCVTIMEO_OLD     0x2000
+#define SO_SNDTIMEO_OLD     0x4000
 #define SO_ACCEPTCONN  0x8000
 
 #define SO_SNDBUF      0x1001
@@ -33,7 +34,6 @@
 #define SO_PROTOCOL    0x1028
 #define SO_DOMAIN      0x1029
 
-
 /* Linux specific, keep the same. */
 #define SO_NO_CHECK    0x000b
 #define SO_PRIORITY    0x000c
 #define SO_GET_FILTER          SO_ATTACH_FILTER
 
 #define SO_PEERNAME            0x001c
-#define SO_TIMESTAMP           0x001d
-#define SCM_TIMESTAMP          SO_TIMESTAMP
 
 #define SO_PEERSEC             0x001e
 #define SO_PASSSEC             0x001f
-#define SO_TIMESTAMPNS         0x0021
-#define SCM_TIMESTAMPNS                SO_TIMESTAMPNS
 
 #define SO_MARK                        0x0022
 
-#define SO_TIMESTAMPING                0x0023
-#define SCM_TIMESTAMPING       SO_TIMESTAMPING
-
 #define SO_RXQ_OVFL             0x0024
 
 #define SO_WIFI_STATUS         0x0025
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
 #define SO_SECURITY_ENCRYPTION_NETWORK         0x5004
 
+#define SO_TIMESTAMP_OLD         0x001d
+#define SO_TIMESTAMPNS_OLD       0x0021
+#define SO_TIMESTAMPING_OLD      0x0023
+
+#define SO_TIMESTAMP_NEW         0x0046
+#define SO_TIMESTAMPNS_NEW       0x0042
+#define SO_TIMESTAMPING_NEW      0x0043
+
+#define SO_RCVTIMEO_NEW          0x0044
+#define SO_SNDTIMEO_NEW          0x0045
+
+#if !defined(__KERNEL__)
+
+
+#if __BITS_PER_LONG == 64
+#define SO_TIMESTAMP           SO_TIMESTAMP_OLD
+#define SO_TIMESTAMPNS         SO_TIMESTAMPNS_OLD
+#define SO_TIMESTAMPING                SO_TIMESTAMPING_OLD
+
+#define SO_RCVTIMEO            SO_RCVTIMEO_OLD
+#define SO_SNDTIMEO            SO_SNDTIMEO_OLD
+#else
+#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
+#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW)
+#define SO_TIMESTAMPING (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPING_OLD : SO_TIMESTAMPING_NEW)
+
+#define SO_RCVTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_RCVTIMEO_OLD : SO_RCVTIMEO_NEW)
+#define SO_SNDTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_SNDTIMEO_OLD : SO_SNDTIMEO_NEW)
+#endif
+
+#define SCM_TIMESTAMP          SO_TIMESTAMP
+#define SCM_TIMESTAMPNS        SO_TIMESTAMPNS
+#define SCM_TIMESTAMPING       SO_TIMESTAMPING
+
+#endif
+
 #endif /* _ASM_SOCKET_H */
index 1372553..1d1544b 100644 (file)
@@ -28,6 +28,7 @@ generic-y += preempt.h
 generic-y += sections.h
 generic-y += segment.h
 generic-y += serial.h
+generic-y += shmparam.h
 generic-y += sizes.h
 generic-y += syscalls.h
 generic-y += topology.h
index 6c6f630..0febf1a 100644 (file)
@@ -1,5 +1,4 @@
 include include/uapi/asm-generic/Kbuild.asm
 
 generic-y += kvm_para.h
-generic-y += shmparam.h
 generic-y += ucontext.h
index 4b4a7f3..6826143 100644 (file)
@@ -198,7 +198,7 @@ config X86
        select IRQ_FORCED_THREADING
        select NEED_SG_DMA_LENGTH
        select PCI_DOMAINS                      if PCI
-       select PCI_LOCKLESS_CONFIG
+       select PCI_LOCKLESS_CONFIG              if PCI
        select PERF_EVENTS
        select RTC_LIB
        select RTC_MC146818_LIB
@@ -446,12 +446,12 @@ config RETPOLINE
          branches. Requires a compiler with -mindirect-branch=thunk-extern
          support for full protection. The kernel may run slower.
 
-config X86_RESCTRL
-       bool "Resource Control support"
+config X86_CPU_RESCTRL
+       bool "x86 CPU resource control support"
        depends on X86 && (CPU_SUP_INTEL || CPU_SUP_AMD)
        select KERNFS
        help
-         Enable Resource Control support.
+         Enable x86 CPU resource control support.
 
          Provide support for the allocation and monitoring of system resources
          usage by the CPU.
index 6403789..f62e347 100644 (file)
@@ -600,6 +600,16 @@ ENTRY(trampoline_32bit_src)
        leal    TRAMPOLINE_32BIT_PGTABLE_OFFSET(%ecx), %eax
        movl    %eax, %cr3
 3:
+       /* Set EFER.LME=1 as a precaution in case hypervsior pulls the rug */
+       pushl   %ecx
+       pushl   %edx
+       movl    $MSR_EFER, %ecx
+       rdmsr
+       btsl    $_EFER_LME, %eax
+       wrmsr
+       popl    %edx
+       popl    %ecx
+
        /* Enable PAE and LA57 (if required) paging modes */
        movl    $X86_CR4_PAE, %eax
        cmpl    $0, %edx
index 91f7563..6ff7e81 100644 (file)
@@ -6,7 +6,7 @@
 #define TRAMPOLINE_32BIT_PGTABLE_OFFSET        0
 
 #define TRAMPOLINE_32BIT_CODE_OFFSET   PAGE_SIZE
-#define TRAMPOLINE_32BIT_CODE_SIZE     0x60
+#define TRAMPOLINE_32BIT_CODE_SIZE     0x70
 
 #define TRAMPOLINE_32BIT_STACK_END     TRAMPOLINE_32BIT_SIZE
 
index 8eaf895..3991377 100644 (file)
@@ -361,7 +361,8 @@ ENTRY(entry_INT80_compat)
 
        /* Need to switch before accessing the thread stack. */
        SWITCH_TO_KERNEL_CR3 scratch_reg=%rdi
-       movq    %rsp, %rdi
+       /* In the Xen PV case we already run on the thread stack. */
+       ALTERNATIVE "movq %rsp, %rdi", "jmp .Lint80_keep_stack", X86_FEATURE_XENPV
        movq    PER_CPU_VAR(cpu_current_top_of_stack), %rsp
 
        pushq   6*8(%rdi)               /* regs->ss */
@@ -370,8 +371,9 @@ ENTRY(entry_INT80_compat)
        pushq   3*8(%rdi)               /* regs->cs */
        pushq   2*8(%rdi)               /* regs->ip */
        pushq   1*8(%rdi)               /* regs->orig_ax */
-
        pushq   (%rdi)                  /* pt_regs->di */
+.Lint80_keep_stack:
+
        pushq   %rsi                    /* pt_regs->si */
        xorl    %esi, %esi              /* nospec   si */
        pushq   %rdx                    /* pt_regs->dx */
index 40e12cf..daafb89 100644 (file)
@@ -3559,6 +3559,14 @@ static void free_excl_cntrs(int cpu)
 
 static void intel_pmu_cpu_dying(int cpu)
 {
+       fini_debug_store_on_cpu(cpu);
+
+       if (x86_pmu.counter_freezing)
+               disable_counter_freeze();
+}
+
+static void intel_pmu_cpu_dead(int cpu)
+{
        struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
        struct intel_shared_regs *pc;
 
@@ -3570,11 +3578,6 @@ static void intel_pmu_cpu_dying(int cpu)
        }
 
        free_excl_cntrs(cpu);
-
-       fini_debug_store_on_cpu(cpu);
-
-       if (x86_pmu.counter_freezing)
-               disable_counter_freeze();
 }
 
 static void intel_pmu_sched_task(struct perf_event_context *ctx,
@@ -3663,6 +3666,7 @@ static __initconst const struct x86_pmu core_pmu = {
        .cpu_prepare            = intel_pmu_cpu_prepare,
        .cpu_starting           = intel_pmu_cpu_starting,
        .cpu_dying              = intel_pmu_cpu_dying,
+       .cpu_dead               = intel_pmu_cpu_dead,
 };
 
 static struct attribute *intel_pmu_attrs[];
@@ -3703,6 +3707,8 @@ static __initconst const struct x86_pmu intel_pmu = {
        .cpu_prepare            = intel_pmu_cpu_prepare,
        .cpu_starting           = intel_pmu_cpu_starting,
        .cpu_dying              = intel_pmu_cpu_dying,
+       .cpu_dead               = intel_pmu_cpu_dead,
+
        .guest_get_msrs         = intel_guest_get_msrs,
        .sched_task             = intel_pmu_sched_task,
 };
index c07bee3..b10e043 100644 (file)
@@ -1222,6 +1222,8 @@ static struct pci_driver snbep_uncore_pci_driver = {
        .id_table       = snbep_uncore_pci_ids,
 };
 
+#define NODE_ID_MASK   0x7
+
 /*
  * build pci bus to socket mapping
  */
@@ -1243,7 +1245,7 @@ static int snbep_pci2phy_map_init(int devid, int nodeid_loc, int idmap_loc, bool
                err = pci_read_config_dword(ubox_dev, nodeid_loc, &config);
                if (err)
                        break;
-               nodeid = config;
+               nodeid = config & NODE_ID_MASK;
                /* get the Node ID mapping */
                err = pci_read_config_dword(ubox_dev, idmap_loc, &config);
                if (err)
index 0dd6b0f..d9a9993 100644 (file)
@@ -6,7 +6,7 @@
  * "Big Core" Processors (Branded as Core, Xeon, etc...)
  *
  * The "_X" parts are generally the EP and EX Xeons, or the
- * "Extreme" ones, like Broadwell-E.
+ * "Extreme" ones, like Broadwell-E, or Atom microserver.
  *
  * While adding a new CPUID for a new microarchitecture, add a new
  * group to keep logically sorted out in chronological order. Within
@@ -71,6 +71,7 @@
 #define INTEL_FAM6_ATOM_GOLDMONT       0x5C /* Apollo Lake */
 #define INTEL_FAM6_ATOM_GOLDMONT_X     0x5F /* Denverton */
 #define INTEL_FAM6_ATOM_GOLDMONT_PLUS  0x7A /* Gemini Lake */
+#define INTEL_FAM6_ATOM_TREMONT_X      0x86 /* Jacobsville */
 
 /* Xeon Phi */
 
index 0ca5061..19d18fa 100644 (file)
@@ -178,6 +178,10 @@ static inline void switch_ldt(struct mm_struct *prev, struct mm_struct *next)
 
 void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk);
 
+/*
+ * Init a new mm.  Used on mm copies, like at fork()
+ * and on mm's that are brand-new, like at execve().
+ */
 static inline int init_new_context(struct task_struct *tsk,
                                   struct mm_struct *mm)
 {
@@ -228,8 +232,22 @@ do {                                               \
 } while (0)
 #endif
 
+static inline void arch_dup_pkeys(struct mm_struct *oldmm,
+                                 struct mm_struct *mm)
+{
+#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
+       if (!cpu_feature_enabled(X86_FEATURE_OSPKE))
+               return;
+
+       /* Duplicate the oldmm pkey state in mm: */
+       mm->context.pkey_allocation_map = oldmm->context.pkey_allocation_map;
+       mm->context.execute_only_pkey   = oldmm->context.execute_only_pkey;
+#endif
+}
+
 static inline int arch_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm)
 {
+       arch_dup_pkeys(oldmm, mm);
        paravirt_arch_dup_mmap(oldmm, mm);
        return ldt_dup_context(oldmm, mm);
 }
index 8f65728..0ce558a 100644 (file)
@@ -7,7 +7,11 @@
 #endif
 
 #ifdef CONFIG_KASAN
+#ifdef CONFIG_KASAN_EXTRA
+#define KASAN_STACK_ORDER 2
+#else
 #define KASAN_STACK_ORDER 1
+#endif
 #else
 #define KASAN_STACK_ORDER 0
 #endif
index 40616e8..2779ace 100644 (file)
@@ -1065,7 +1065,7 @@ static inline void native_set_pte_at(struct mm_struct *mm, unsigned long addr,
 static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr,
                              pmd_t *pmdp, pmd_t pmd)
 {
-       native_set_pmd(pmdp, pmd);
+       set_pmd(pmdp, pmd);
 }
 
 static inline void set_pud_at(struct mm_struct *mm, unsigned long addr,
index 40ebddd..f6b7fe2 100644 (file)
@@ -2,7 +2,7 @@
 #ifndef _ASM_X86_RESCTRL_SCHED_H
 #define _ASM_X86_RESCTRL_SCHED_H
 
-#ifdef CONFIG_X86_RESCTRL
+#ifdef CONFIG_X86_CPU_RESCTRL
 
 #include <linux/sched.h>
 #include <linux/jump_label.h>
@@ -88,6 +88,6 @@ static inline void resctrl_sched_in(void)
 
 static inline void resctrl_sched_in(void) {}
 
-#endif /* CONFIG_X86_RESCTRL */
+#endif /* CONFIG_X86_CPU_RESCTRL */
 
 #endif /* _ASM_X86_RESCTRL_SCHED_H */
index f6648e9..efe701b 100644 (file)
@@ -3,3 +3,4 @@ include include/uapi/asm-generic/Kbuild.asm
 generated-y += unistd_32.h
 generated-y += unistd_64.h
 generated-y += unistd_x32.h
+generic-y += socket.h
diff --git a/arch/x86/include/uapi/asm/socket.h b/arch/x86/include/uapi/asm/socket.h
deleted file mode 100644 (file)
index 6b71384..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/socket.h>
index b6fa086..cfd24f9 100644 (file)
@@ -39,7 +39,7 @@ obj-$(CONFIG_CPU_SUP_UMC_32)          += umc.o
 obj-$(CONFIG_X86_MCE)                  += mce/
 obj-$(CONFIG_MTRR)                     += mtrr/
 obj-$(CONFIG_MICROCODE)                        += microcode/
-obj-$(CONFIG_X86_RESCTRL)              += resctrl/
+obj-$(CONFIG_X86_CPU_RESCTRL)          += resctrl/
 
 obj-$(CONFIG_X86_LOCAL_APIC)           += perfctr-watchdog.o
 
index 1de0f41..01874d5 100644 (file)
@@ -71,7 +71,7 @@ void __init check_bugs(void)
         * identify_boot_cpu() initialized SMT support information, let the
         * core code know.
         */
-       cpu_smt_check_topology_early();
+       cpu_smt_check_topology();
 
        if (!IS_ENABLED(CONFIG_SMP)) {
                pr_info("CPU: ");
index 672c722..6ce290c 100644 (file)
@@ -784,6 +784,7 @@ static int mce_no_way_out(struct mce *m, char **msg, unsigned long *validp,
                        quirk_no_way_out(i, m, regs);
 
                if (mce_severity(m, mca_cfg.tolerant, &tmp, true) >= MCE_PANIC_SEVERITY) {
+                       m->bank = i;
                        mce_read_aux(m, i);
                        *msg = tmp;
                        return 1;
index 51adde0..e1f3ba1 100644 (file)
@@ -855,7 +855,7 @@ load_microcode_amd(bool save, u8 family, const u8 *data, size_t size)
        if (!p) {
                return ret;
        } else {
-               if (boot_cpu_data.microcode == p->patch_id)
+               if (boot_cpu_data.microcode >= p->patch_id)
                        return ret;
 
                ret = UCODE_NEW;
index 1cabe6f..4a06c37 100644 (file)
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_X86_RESCTRL)      += core.o rdtgroup.o monitor.o
-obj-$(CONFIG_X86_RESCTRL)      += ctrlmondata.o pseudo_lock.o
+obj-$(CONFIG_X86_CPU_RESCTRL)  += core.o rdtgroup.o monitor.o
+obj-$(CONFIG_X86_CPU_RESCTRL)  += ctrlmondata.o pseudo_lock.o
 CFLAGS_pseudo_lock.o = -I$(src)
index c8b07d8..17ffc86 100644 (file)
@@ -470,6 +470,7 @@ int crash_load_segments(struct kimage *image)
 
        kbuf.memsz = kbuf.bufsz;
        kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
+       kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
        ret = kexec_add_buffer(&kbuf);
        if (ret) {
                vfree((void *)image->arch.elf_headers);
index b0acb22..dfd3aca 100644 (file)
 
 #define HPET_MASK                      CLOCKSOURCE_MASK(32)
 
-/* FSEC = 10^-15
-   NSEC = 10^-9 */
-#define FSEC_PER_NSEC                  1000000L
-
 #define HPET_DEV_USED_BIT              2
 #define HPET_DEV_USED                  (1 << HPET_DEV_USED_BIT)
 #define HPET_DEV_VALID                 0x8
index 278cd07..53917a3 100644 (file)
@@ -167,6 +167,9 @@ setup_efi_state(struct boot_params *params, unsigned long params_load_addr,
        struct efi_info *current_ei = &boot_params.efi_info;
        struct efi_info *ei = &params->efi_info;
 
+       if (!efi_enabled(EFI_RUNTIME_SERVICES))
+               return 0;
+
        if (!current_ei->efi_memmap_size)
                return 0;
 
@@ -434,6 +437,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
        kbuf.memsz = PAGE_ALIGN(header->init_size);
        kbuf.buf_align = header->kernel_alignment;
        kbuf.buf_min = MIN_KERNEL_LOAD_ADDR;
+       kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
        ret = kexec_add_buffer(&kbuf);
        if (ret)
                goto out_free_params;
@@ -448,6 +452,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
                kbuf.bufsz = kbuf.memsz = initrd_len;
                kbuf.buf_align = PAGE_SIZE;
                kbuf.buf_min = MIN_INITRD_LOAD_ADDR;
+               kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
                ret = kexec_add_buffer(&kbuf);
                if (ret)
                        goto out_free_params;
index e9f777b..3fae238 100644 (file)
@@ -297,15 +297,16 @@ static int __init tsc_setup(char *str)
 
 __setup("tsc=", tsc_setup);
 
-#define MAX_RETRIES     5
-#define SMI_TRESHOLD    50000
+#define MAX_RETRIES            5
+#define TSC_DEFAULT_THRESHOLD  0x20000
 
 /*
- * Read TSC and the reference counters. Take care of SMI disturbance
+ * Read TSC and the reference counters. Take care of any disturbances
  */
 static u64 tsc_read_refs(u64 *p, int hpet)
 {
        u64 t1, t2;
+       u64 thresh = tsc_khz ? tsc_khz >> 5 : TSC_DEFAULT_THRESHOLD;
        int i;
 
        for (i = 0; i < MAX_RETRIES; i++) {
@@ -315,7 +316,7 @@ static u64 tsc_read_refs(u64 *p, int hpet)
                else
                        *p = acpi_pm_read_early();
                t2 = get_cycles();
-               if ((t2 - t1) < SMI_TRESHOLD)
+               if ((t2 - t1) < thresh)
                        return t2;
        }
        return ULLONG_MAX;
@@ -703,15 +704,15 @@ static unsigned long pit_hpet_ptimer_calibrate_cpu(void)
         * zero. In each wait loop iteration we read the TSC and check
         * the delta to the previous read. We keep track of the min
         * and max values of that delta. The delta is mostly defined
-        * by the IO time of the PIT access, so we can detect when a
-        * SMI/SMM disturbance happened between the two reads. If the
+        * by the IO time of the PIT access, so we can detect when
+        * any disturbance happened between the two reads. If the
         * maximum time is significantly larger than the minimum time,
         * then we discard the result and have another try.
         *
         * 2) Reference counter. If available we use the HPET or the
         * PMTIMER as a reference to check the sanity of that value.
         * We use separate TSC readouts and check inside of the
-        * reference read for a SMI/SMM disturbance. We dicard
+        * reference read for any possible disturbance. We dicard
         * disturbed values here as well. We do that around the PIT
         * calibration delay loop as we have to wait for a certain
         * amount of time anyway.
@@ -744,7 +745,7 @@ static unsigned long pit_hpet_ptimer_calibrate_cpu(void)
                if (ref1 == ref2)
                        continue;
 
-               /* Check, whether the sampling was disturbed by an SMI */
+               /* Check, whether the sampling was disturbed */
                if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX)
                        continue;
 
@@ -1268,7 +1269,7 @@ static DECLARE_DELAYED_WORK(tsc_irqwork, tsc_refine_calibration_work);
  */
 static void tsc_refine_calibration_work(struct work_struct *work)
 {
-       static u64 tsc_start = -1, ref_start;
+       static u64 tsc_start = ULLONG_MAX, ref_start;
        static int hpet;
        u64 tsc_stop, ref_stop, delta;
        unsigned long freq;
@@ -1283,14 +1284,15 @@ static void tsc_refine_calibration_work(struct work_struct *work)
         * delayed the first time we expire. So set the workqueue
         * again once we know timers are working.
         */
-       if (tsc_start == -1) {
+       if (tsc_start == ULLONG_MAX) {
+restart:
                /*
                 * Only set hpet once, to avoid mixing hardware
                 * if the hpet becomes enabled later.
                 */
                hpet = is_hpet_enabled();
-               schedule_delayed_work(&tsc_irqwork, HZ);
                tsc_start = tsc_read_refs(&ref_start, hpet);
+               schedule_delayed_work(&tsc_irqwork, HZ);
                return;
        }
 
@@ -1300,9 +1302,9 @@ static void tsc_refine_calibration_work(struct work_struct *work)
        if (ref_start == ref_stop)
                goto out;
 
-       /* Check, whether the sampling was disturbed by an SMI */
-       if (tsc_start == ULLONG_MAX || tsc_stop == ULLONG_MAX)
-               goto out;
+       /* Check, whether the sampling was disturbed */
+       if (tsc_stop == ULLONG_MAX)
+               goto restart;
 
        delta = tsc_stop - tsc_start;
        delta *= 1000000LL;
index 8ff2052..d8ea4eb 100644 (file)
@@ -211,6 +211,7 @@ static void free_nested(struct kvm_vcpu *vcpu)
        if (!vmx->nested.vmxon && !vmx->nested.smm.vmxon)
                return;
 
+       hrtimer_cancel(&vmx->nested.preemption_timer);
        vmx->nested.vmxon = false;
        vmx->nested.smm.vmxon = false;
        free_vpid(vmx->nested.vpid02);
index 4341175..95d6180 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/mod_devicetable.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
+#include <linux/sched/smt.h>
 #include <linux/slab.h>
 #include <linux/tboot.h>
 #include <linux/trace_events.h>
@@ -6823,7 +6824,7 @@ static int vmx_vm_init(struct kvm *kvm)
                         * Warn upon starting the first VM in a potentially
                         * insecure environment.
                         */
-                       if (cpu_smt_control == CPU_SMT_ENABLED)
+                       if (sched_smt_active())
                                pr_warn_once(L1TF_MSG_SMT);
                        if (l1tf_vmx_mitigation == VMENTER_L1D_FLUSH_NEVER)
                                pr_warn_once(L1TF_MSG_L1D);
index 3d27206..e67ecf2 100644 (file)
@@ -5116,6 +5116,13 @@ int kvm_read_guest_virt(struct kvm_vcpu *vcpu,
 {
        u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
 
+       /*
+        * FIXME: this should call handle_emulation_failure if X86EMUL_IO_NEEDED
+        * is returned, but our callers are not ready for that and they blindly
+        * call kvm_inject_page_fault.  Ensure that they at least do not leak
+        * uninitialized kernel stack memory into cr2 and error code.
+        */
+       memset(exception, 0, sizeof(*exception));
        return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, access,
                                          exception);
 }
index 6689467..df50451 100644 (file)
@@ -2,8 +2,11 @@
 #include <linux/module.h>
 #include <linux/io.h>
 
+#define movs(type,to,from) \
+       asm volatile("movs" type:"=&D" (to), "=&S" (from):"0" (to), "1" (from):"memory")
+
 /* Originally from i386/string.h */
-static __always_inline void __iomem_memcpy(void *to, const void *from, size_t n)
+static __always_inline void rep_movs(void *to, const void *from, size_t n)
 {
        unsigned long d0, d1, d2;
        asm volatile("rep ; movsl\n\t"
@@ -21,13 +24,37 @@ static __always_inline void __iomem_memcpy(void *to, const void *from, size_t n)
 
 void memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
 {
-       __iomem_memcpy(to, (const void *)from, n);
+       if (unlikely(!n))
+               return;
+
+       /* Align any unaligned source IO */
+       if (unlikely(1 & (unsigned long)from)) {
+               movs("b", to, from);
+               n--;
+       }
+       if (n > 1 && unlikely(2 & (unsigned long)from)) {
+               movs("w", to, from);
+               n-=2;
+       }
+       rep_movs(to, (const void *)from, n);
 }
 EXPORT_SYMBOL(memcpy_fromio);
 
 void memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
 {
-       __iomem_memcpy((void *)to, (const void *) from, n);
+       if (unlikely(!n))
+               return;
+
+       /* Align any unaligned destination IO */
+       if (unlikely(1 & (unsigned long)to)) {
+               movs("b", to, from);
+               n--;
+       }
+       if (n > 1 && unlikely(2 & (unsigned long)to)) {
+               movs("w", to, from);
+               n-=2;
+       }
+       rep_movs((void *)to, (const void *) from, n);
 }
 EXPORT_SYMBOL(memcpy_toio);
 
index 79778ab..a536651 100644 (file)
@@ -36,8 +36,8 @@ static inline u16 i8254(void)
        u16 status, timer;
 
        do {
-               outb(I8254_PORT_CONTROL,
-                    I8254_CMD_READBACK | I8254_SELECT_COUNTER0);
+               outb(I8254_CMD_READBACK | I8254_SELECT_COUNTER0,
+                    I8254_PORT_CONTROL);
                status = inb(I8254_PORT_COUNTER0);
                timer  = inb(I8254_PORT_COUNTER0);
                timer |= inb(I8254_PORT_COUNTER0) << 8;
index 2ff25ad..9d5c75f 100644 (file)
@@ -595,7 +595,7 @@ static void show_ldttss(const struct desc_ptr *gdt, const char *name, u16 index)
                return;
        }
 
-       addr = desc.base0 | (desc.base1 << 16) | (desc.base2 << 24);
+       addr = desc.base0 | (desc.base1 << 16) | ((unsigned long)desc.base2 << 24);
 #ifdef CONFIG_X86_64
        addr |= ((u64)desc.base3 << 32);
 #endif
index a19ef1a..4aa9b14 100644 (file)
@@ -158,8 +158,8 @@ static void __init sme_populate_pgd(struct sme_populate_pgd_data *ppd)
        pmd = pmd_offset(pud, ppd->vaddr);
        if (pmd_none(*pmd)) {
                pte = ppd->pgtable_area;
-               memset(pte, 0, sizeof(pte) * PTRS_PER_PTE);
-               ppd->pgtable_area += sizeof(pte) * PTRS_PER_PTE;
+               memset(pte, 0, sizeof(*pte) * PTRS_PER_PTE);
+               ppd->pgtable_area += sizeof(*pte) * PTRS_PER_PTE;
                set_pmd(pmd, __pmd(PMD_FLAGS | __pa(pte)));
        }
 
index 4f89723..14e6119 100644 (file)
@@ -230,6 +230,29 @@ static bool __cpa_pfn_in_highmap(unsigned long pfn)
 
 #endif
 
+/*
+ * See set_mce_nospec().
+ *
+ * Machine check recovery code needs to change cache mode of poisoned pages to
+ * UC to avoid speculative access logging another error. But passing the
+ * address of the 1:1 mapping to set_memory_uc() is a fine way to encourage a
+ * speculative access. So we cheat and flip the top bit of the address. This
+ * works fine for the code that updates the page tables. But at the end of the
+ * process we need to flush the TLB and cache and the non-canonical address
+ * causes a #GP fault when used by the INVLPG and CLFLUSH instructions.
+ *
+ * But in the common case we already have a canonical address. This code
+ * will fix the top bit if needed and is a no-op otherwise.
+ */
+static inline unsigned long fix_addr(unsigned long addr)
+{
+#ifdef CONFIG_X86_64
+       return (long)(addr << 1) >> 1;
+#else
+       return addr;
+#endif
+}
+
 static unsigned long __cpa_addr(struct cpa_data *cpa, unsigned long idx)
 {
        if (cpa->flags & CPA_PAGES_ARRAY) {
@@ -313,7 +336,7 @@ void __cpa_flush_tlb(void *data)
        unsigned int i;
 
        for (i = 0; i < cpa->numpages; i++)
-               __flush_tlb_one_kernel(__cpa_addr(cpa, i));
+               __flush_tlb_one_kernel(fix_addr(__cpa_addr(cpa, i)));
 }
 
 static void cpa_flush(struct cpa_data *data, int cache)
@@ -347,7 +370,7 @@ static void cpa_flush(struct cpa_data *data, int cache)
                 * Only flush present addresses:
                 */
                if (pte && (pte_val(*pte) & _PAGE_PRESENT))
-                       clflush_cache_range_opt((void *)addr, PAGE_SIZE);
+                       clflush_cache_range_opt((void *)fix_addr(addr), PAGE_SIZE);
        }
        mb();
 }
@@ -1627,29 +1650,6 @@ out:
        return ret;
 }
 
-/*
- * Machine check recovery code needs to change cache mode of poisoned
- * pages to UC to avoid speculative access logging another error. But
- * passing the address of the 1:1 mapping to set_memory_uc() is a fine
- * way to encourage a speculative access. So we cheat and flip the top
- * bit of the address. This works fine for the code that updates the
- * page tables. But at the end of the process we need to flush the cache
- * and the non-canonical address causes a #GP fault when used by the
- * CLFLUSH instruction.
- *
- * But in the common case we already have a canonical address. This code
- * will fix the top bit if needed and is a no-op otherwise.
- */
-static inline unsigned long make_addr_canonical_again(unsigned long addr)
-{
-#ifdef CONFIG_X86_64
-       return (long)(addr << 1) >> 1;
-#else
-       return addr;
-#endif
-}
-
-
 static int change_page_attr_set_clr(unsigned long *addr, int numpages,
                                    pgprot_t mask_set, pgprot_t mask_clr,
                                    int force_split, int in_flag,
index 20a0756..ce91682 100644 (file)
@@ -164,7 +164,7 @@ config XTENSA_FAKE_NMI
          If unsure, say N.
 
 config XTENSA_UNALIGNED_USER
-       bool "Unaligned memory access in use space"
+       bool "Unaligned memory access in user space"
        help
          The Xtensa architecture currently does not handle unaligned
          memory accesses in hardware but through an exception handler.
@@ -451,7 +451,7 @@ config USE_OF
        help
          Include support for flattened device tree machine descriptions.
 
-config BUILTIN_DTB
+config BUILTIN_DTB_SOURCE
        string "DTB to build into the kernel image"
        depends on OF
 
index f8052ba..0b8d00c 100644 (file)
@@ -7,9 +7,9 @@
 #
 #
 
-BUILTIN_DTB := $(patsubst "%",%,$(CONFIG_BUILTIN_DTB)).dtb.o
-ifneq ($(CONFIG_BUILTIN_DTB),"")
-obj-$(CONFIG_OF) += $(BUILTIN_DTB)
+BUILTIN_DTB_SOURCE := $(patsubst "%",%,$(CONFIG_BUILTIN_DTB_SOURCE)).dtb.o
+ifneq ($(CONFIG_BUILTIN_DTB_SOURCE),"")
+obj-$(CONFIG_OF) += $(BUILTIN_DTB_SOURCE)
 endif
 
 # for CONFIG_OF_ALL_DTBS test
index 2bf964d..f378e56 100644 (file)
@@ -34,7 +34,7 @@ CONFIG_XTENSA_PLATFORM_XTFPGA=y
 CONFIG_CMDLINE_BOOL=y
 CONFIG_CMDLINE="earlycon=uart8250,mmio32native,0xfd050020,115200n8 console=ttyS0,115200n8 ip=dhcp root=/dev/nfs rw debug memmap=0x38000000@0"
 CONFIG_USE_OF=y
-CONFIG_BUILTIN_DTB="kc705"
+CONFIG_BUILTIN_DTB_SOURCE="kc705"
 # CONFIG_COMPACTION is not set
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
 CONFIG_PM=y
index 3221b70..62f32a9 100644 (file)
@@ -38,7 +38,7 @@ CONFIG_HIGHMEM=y
 # CONFIG_PCI is not set
 CONFIG_XTENSA_PLATFORM_XTFPGA=y
 CONFIG_USE_OF=y
-CONFIG_BUILTIN_DTB="csp"
+CONFIG_BUILTIN_DTB_SOURCE="csp"
 # CONFIG_COMPACTION is not set
 CONFIG_XTFPGA_LCD=y
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
index 985fa85..8bebe07 100644 (file)
@@ -33,7 +33,7 @@ CONFIG_XTENSA_PLATFORM_XTFPGA=y
 CONFIG_CMDLINE_BOOL=y
 CONFIG_CMDLINE="earlycon=uart8250,mmio32native,0xfd050020,115200n8 console=ttyS0,115200n8 ip=dhcp root=/dev/nfs rw debug memmap=0x38000000@0"
 CONFIG_USE_OF=y
-CONFIG_BUILTIN_DTB="kc705"
+CONFIG_BUILTIN_DTB_SOURCE="kc705"
 # CONFIG_COMPACTION is not set
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
 CONFIG_NET=y
index f3fc4f9..933ab2a 100644 (file)
@@ -39,7 +39,7 @@ CONFIG_XTENSA_PLATFORM_XTFPGA=y
 CONFIG_CMDLINE_BOOL=y
 CONFIG_CMDLINE="earlycon=uart8250,mmio32native,0x9d050020,115200n8 console=ttyS0,115200n8 ip=dhcp root=/dev/nfs rw debug memmap=256M@0x60000000"
 CONFIG_USE_OF=y
-CONFIG_BUILTIN_DTB="kc705_nommu"
+CONFIG_BUILTIN_DTB_SOURCE="kc705_nommu"
 CONFIG_BINFMT_FLAT=y
 CONFIG_NET=y
 CONFIG_PACKET=y
index 11fed6c..e29c5b1 100644 (file)
@@ -33,11 +33,12 @@ CONFIG_SMP=y
 CONFIG_HOTPLUG_CPU=y
 # CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX is not set
 # CONFIG_PCI is not set
+CONFIG_VECTORS_OFFSET=0x00002000
 CONFIG_XTENSA_PLATFORM_XTFPGA=y
 CONFIG_CMDLINE_BOOL=y
 CONFIG_CMDLINE="earlycon=uart8250,mmio32native,0xfd050020,115200n8 console=ttyS0,115200n8 ip=dhcp root=/dev/nfs rw debug memmap=96M@0"
 CONFIG_USE_OF=y
-CONFIG_BUILTIN_DTB="lx200mx"
+CONFIG_BUILTIN_DTB_SOURCE="lx200mx"
 # CONFIG_COMPACTION is not set
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
 CONFIG_NET=y
index e255683..809f39c 100644 (file)
@@ -25,6 +25,7 @@ generic-y += percpu.h
 generic-y += preempt.h
 generic-y += rwsem.h
 generic-y += sections.h
+generic-y += socket.h
 generic-y += topology.h
 generic-y += trace_clock.h
 generic-y += vga.h
index 960bf1e..6b43e50 100644 (file)
@@ -2,3 +2,4 @@ include include/uapi/asm-generic/Kbuild.asm
 
 generated-y += unistd_32.h
 generic-y += kvm_para.h
+generic-y += socket.h
diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h
deleted file mode 100644 (file)
index b434217..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*
- * include/asm-xtensa/socket.h
- *
- * Copied from i386.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#ifndef _XTENSA_SOCKET_H
-#define _XTENSA_SOCKET_H
-
-#include <asm/sockios.h>
-
-/* For setsockoptions(2) */
-#define SOL_SOCKET     1
-
-#define SO_DEBUG       1
-#define SO_REUSEADDR   2
-#define SO_TYPE                3
-#define SO_ERROR       4
-#define SO_DONTROUTE   5
-#define SO_BROADCAST   6
-#define SO_SNDBUF      7
-#define SO_RCVBUF      8
-#define SO_SNDBUFFORCE 32
-#define SO_RCVBUFFORCE 33
-#define SO_KEEPALIVE   9
-#define SO_OOBINLINE   10
-#define SO_NO_CHECK    11
-#define SO_PRIORITY    12
-#define SO_LINGER      13
-#define SO_BSDCOMPAT   14
-#define SO_REUSEPORT   15
-#define SO_PASSCRED    16
-#define SO_PEERCRED    17
-#define SO_RCVLOWAT    18
-#define SO_SNDLOWAT    19
-#define SO_RCVTIMEO    20
-#define SO_SNDTIMEO    21
-
-/* Security levels - as per NRL IPv6 - don't actually do anything */
-
-#define SO_SECURITY_AUTHENTICATION             22
-#define SO_SECURITY_ENCRYPTION_TRANSPORT       23
-#define SO_SECURITY_ENCRYPTION_NETWORK         24
-
-#define SO_BINDTODEVICE        25
-
-/* Socket filtering */
-
-#define SO_ATTACH_FILTER        26
-#define SO_DETACH_FILTER        27
-#define SO_GET_FILTER          SO_ATTACH_FILTER
-
-#define SO_PEERNAME            28
-#define SO_TIMESTAMP           29
-#define SCM_TIMESTAMP          SO_TIMESTAMP
-
-#define SO_ACCEPTCONN          30
-#define SO_PEERSEC             31
-#define SO_PASSSEC             34
-#define SO_TIMESTAMPNS         35
-#define SCM_TIMESTAMPNS                SO_TIMESTAMPNS
-
-#define SO_MARK                        36
-
-#define SO_TIMESTAMPING                37
-#define SCM_TIMESTAMPING       SO_TIMESTAMPING
-
-#define SO_PROTOCOL            38
-#define SO_DOMAIN              39
-
-#define SO_RXQ_OVFL             40
-
-#define SO_WIFI_STATUS         41
-#define SCM_WIFI_STATUS                SO_WIFI_STATUS
-#define SO_PEEK_OFF            42
-
-/* Instruct lower device to use last 4-bytes of skb data as FCS */
-#define SO_NOFCS               43
-
-#define SO_LOCK_FILTER         44
-
-#define SO_SELECT_ERR_QUEUE    45
-
-#define SO_BUSY_POLL           46
-
-#define SO_MAX_PACING_RATE     47
-
-#define SO_BPF_EXTENSIONS      48
-
-#define SO_INCOMING_CPU                49
-
-#define SO_ATTACH_BPF          50
-#define SO_DETACH_BPF          SO_DETACH_FILTER
-
-#define SO_ATTACH_REUSEPORT_CBPF       51
-#define SO_ATTACH_REUSEPORT_EBPF       52
-
-#define SO_CNX_ADVICE          53
-
-#define SCM_TIMESTAMPING_OPT_STATS     54
-
-#define SO_MEMINFO             55
-
-#define SO_INCOMING_NAPI_ID    56
-
-#define SO_COOKIE              57
-
-#define SCM_TIMESTAMPING_PKTINFO       58
-
-#define SO_PEERGROUPS          59
-
-#define SO_ZEROCOPY            60
-
-#define SO_TXTIME              61
-#define SCM_TXTIME             SO_TXTIME
-
-#define SO_BINDTOIFINDEX       62
-
-#endif /* _XTENSA_SOCKET_H */
index da08e75..7f00971 100644 (file)
@@ -276,12 +276,13 @@ should_never_return:
 
        movi    a2, cpu_start_ccount
 1:
+       memw
        l32i    a3, a2, 0
        beqi    a3, 0, 1b
        movi    a3, 0
        s32i    a3, a2, 0
-       memw
 1:
+       memw
        l32i    a3, a2, 0
        beqi    a3, 0, 1b
        wsr     a3, ccount
@@ -317,11 +318,13 @@ ENTRY(cpu_restart)
        rsr     a0, prid
        neg     a2, a0
        movi    a3, cpu_start_id
+       memw
        s32i    a2, a3, 0
 #if XCHAL_DCACHE_IS_WRITEBACK
        dhwbi   a3, 0
 #endif
 1:
+       memw
        l32i    a2, a3, 0
        dhi     a3, 0
        bne     a2, a0, 1b
index 932d646..be1f280 100644 (file)
@@ -83,7 +83,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
 {
        unsigned i;
 
-       for (i = 0; i < max_cpus; ++i)
+       for_each_possible_cpu(i)
                set_cpu_present(i, true);
 }
 
@@ -96,6 +96,11 @@ void __init smp_init_cpus(void)
        pr_info("%s: Core Count = %d\n", __func__, ncpus);
        pr_info("%s: Core Id = %d\n", __func__, core_id);
 
+       if (ncpus > NR_CPUS) {
+               ncpus = NR_CPUS;
+               pr_info("%s: limiting core count by %d\n", __func__, ncpus);
+       }
+
        for (i = 0; i < ncpus; ++i)
                set_cpu_possible(i, true);
 }
@@ -195,9 +200,11 @@ static int boot_secondary(unsigned int cpu, struct task_struct *ts)
        int i;
 
 #ifdef CONFIG_HOTPLUG_CPU
-       cpu_start_id = cpu;
-       system_flush_invalidate_dcache_range(
-                       (unsigned long)&cpu_start_id, sizeof(cpu_start_id));
+       WRITE_ONCE(cpu_start_id, cpu);
+       /* Pairs with the third memw in the cpu_restart */
+       mb();
+       system_flush_invalidate_dcache_range((unsigned long)&cpu_start_id,
+                                            sizeof(cpu_start_id));
 #endif
        smp_call_function_single(0, mx_cpu_start, (void *)cpu, 1);
 
@@ -206,18 +213,21 @@ static int boot_secondary(unsigned int cpu, struct task_struct *ts)
                        ccount = get_ccount();
                while (!ccount);
 
-               cpu_start_ccount = ccount;
+               WRITE_ONCE(cpu_start_ccount, ccount);
 
-               while (time_before(jiffies, timeout)) {
+               do {
+                       /*
+                        * Pairs with the first two memws in the
+                        * .Lboot_secondary.
+                        */
                        mb();
-                       if (!cpu_start_ccount)
-                               break;
-               }
+                       ccount = READ_ONCE(cpu_start_ccount);
+               } while (ccount && time_before(jiffies, timeout));
 
-               if (cpu_start_ccount) {
+               if (ccount) {
                        smp_call_function_single(0, mx_cpu_stop,
-                                       (void *)cpu, 1);
-                       cpu_start_ccount = 0;
+                                                (void *)cpu, 1);
+                       WRITE_ONCE(cpu_start_ccount, 0);
                        return -EIO;
                }
        }
@@ -237,6 +247,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
        pr_debug("%s: Calling wakeup_secondary(cpu:%d, idle:%p, sp: %08lx)\n",
                        __func__, cpu, idle, start_info.stack);
 
+       init_completion(&cpu_running);
        ret = boot_secondary(cpu, idle);
        if (ret == 0) {
                wait_for_completion_timeout(&cpu_running,
@@ -298,8 +309,10 @@ void __cpu_die(unsigned int cpu)
        unsigned long timeout = jiffies + msecs_to_jiffies(1000);
        while (time_before(jiffies, timeout)) {
                system_invalidate_dcache_range((unsigned long)&cpu_start_id,
-                               sizeof(cpu_start_id));
-               if (cpu_start_id == -cpu) {
+                                              sizeof(cpu_start_id));
+               /* Pairs with the second memw in the cpu_restart */
+               mb();
+               if (READ_ONCE(cpu_start_id) == -cpu) {
                        platform_cpu_kill(cpu);
                        return;
                }
index fd524a5..378186b 100644 (file)
@@ -89,7 +89,7 @@ static int ccount_timer_shutdown(struct clock_event_device *evt)
                container_of(evt, struct ccount_timer, evt);
 
        if (timer->irq_enabled) {
-               disable_irq(evt->irq);
+               disable_irq_nosync(evt->irq);
                timer->irq_enabled = 0;
        }
        return 0;
index 1ccec27..6b78ec5 100644 (file)
@@ -462,6 +462,10 @@ static void blk_rq_timed_out_timer(struct timer_list *t)
        kblockd_schedule_work(&q->timeout_work);
 }
 
+static void blk_timeout_work(struct work_struct *work)
+{
+}
+
 /**
  * blk_alloc_queue_node - allocate a request queue
  * @gfp_mask: memory allocation flags
@@ -505,7 +509,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
        timer_setup(&q->backing_dev_info->laptop_mode_wb_timer,
                    laptop_mode_timer_fn, 0);
        timer_setup(&q->timeout, blk_rq_timed_out_timer, 0);
-       INIT_WORK(&q->timeout_work, NULL);
+       INIT_WORK(&q->timeout_work, blk_timeout_work);
        INIT_LIST_HEAD(&q->icq_list);
 #ifdef CONFIG_BLK_CGROUP
        INIT_LIST_HEAD(&q->blkg_list);
@@ -1083,18 +1087,7 @@ blk_qc_t generic_make_request(struct bio *bio)
                        /* Create a fresh bio_list for all subordinate requests */
                        bio_list_on_stack[1] = bio_list_on_stack[0];
                        bio_list_init(&bio_list_on_stack[0]);
-
-                       /*
-                        * Since we're recursing into make_request here, ensure
-                        * that we mark this bio as already having entered the queue.
-                        * If not, and the queue is going away, we can get stuck
-                        * forever on waiting for the queue reference to drop. But
-                        * that will never happen, as we're already holding a
-                        * reference to it.
-                        */
-                       bio_set_flag(bio, BIO_QUEUE_ENTERED);
                        ret = q->make_request_fn(q, bio);
-                       bio_clear_flag(bio, BIO_QUEUE_ENTERED);
 
                        /* sort new bios into those for a lower level
                         * and those for the same level
index a3fc719..6e0f2d9 100644 (file)
@@ -335,7 +335,7 @@ static void mq_flush_data_end_io(struct request *rq, blk_status_t error)
        blk_flush_complete_seq(rq, fq, REQ_FSEQ_DATA, error);
        spin_unlock_irqrestore(&fq->mq_flush_lock, flags);
 
-       blk_mq_run_hw_queue(hctx, true);
+       blk_mq_sched_restart(hctx);
 }
 
 /**
index fc714ef..2620baa 100644 (file)
@@ -72,6 +72,7 @@
 #include <linux/sched/loadavg.h>
 #include <linux/sched/signal.h>
 #include <trace/events/block.h>
+#include <linux/blk-mq.h>
 #include "blk-rq-qos.h"
 #include "blk-stat.h"
 
@@ -591,6 +592,7 @@ static void blkcg_iolatency_done_bio(struct rq_qos *rqos, struct bio *bio)
        u64 now = ktime_to_ns(ktime_get());
        bool issue_as_root = bio_issue_as_root_blkg(bio);
        bool enabled = false;
+       int inflight = 0;
 
        blkg = bio->bi_blkg;
        if (!blkg || !bio_flagged(bio, BIO_TRACKED))
@@ -601,6 +603,9 @@ static void blkcg_iolatency_done_bio(struct rq_qos *rqos, struct bio *bio)
                return;
 
        enabled = blk_iolatency_enabled(iolat->blkiolat);
+       if (!enabled)
+               return;
+
        while (blkg && blkg->parent) {
                iolat = blkg_to_lat(blkg);
                if (!iolat) {
@@ -609,8 +614,9 @@ static void blkcg_iolatency_done_bio(struct rq_qos *rqos, struct bio *bio)
                }
                rqw = &iolat->rq_wait;
 
-               atomic_dec(&rqw->inflight);
-               if (!enabled || iolat->min_lat_nsec == 0)
+               inflight = atomic_dec_return(&rqw->inflight);
+               WARN_ON_ONCE(inflight < 0);
+               if (iolat->min_lat_nsec == 0)
                        goto next;
                iolatency_record_time(iolat, &bio->bi_issue, now,
                                      issue_as_root);
@@ -754,10 +760,13 @@ int blk_iolatency_init(struct request_queue *q)
        return 0;
 }
 
-static void iolatency_set_min_lat_nsec(struct blkcg_gq *blkg, u64 val)
+/*
+ * return 1 for enabling iolatency, return -1 for disabling iolatency, otherwise
+ * return 0.
+ */
+static int iolatency_set_min_lat_nsec(struct blkcg_gq *blkg, u64 val)
 {
        struct iolatency_grp *iolat = blkg_to_lat(blkg);
-       struct blk_iolatency *blkiolat = iolat->blkiolat;
        u64 oldval = iolat->min_lat_nsec;
 
        iolat->min_lat_nsec = val;
@@ -766,9 +775,10 @@ static void iolatency_set_min_lat_nsec(struct blkcg_gq *blkg, u64 val)
                                    BLKIOLATENCY_MAX_WIN_SIZE);
 
        if (!oldval && val)
-               atomic_inc(&blkiolat->enabled);
+               return 1;
        if (oldval && !val)
-               atomic_dec(&blkiolat->enabled);
+               return -1;
+       return 0;
 }
 
 static void iolatency_clear_scaling(struct blkcg_gq *blkg)
@@ -800,6 +810,7 @@ static ssize_t iolatency_set_limit(struct kernfs_open_file *of, char *buf,
        u64 lat_val = 0;
        u64 oldval;
        int ret;
+       int enable = 0;
 
        ret = blkg_conf_prep(blkcg, &blkcg_policy_iolatency, buf, &ctx);
        if (ret)
@@ -834,7 +845,12 @@ static ssize_t iolatency_set_limit(struct kernfs_open_file *of, char *buf,
        blkg = ctx.blkg;
        oldval = iolat->min_lat_nsec;
 
-       iolatency_set_min_lat_nsec(blkg, lat_val);
+       enable = iolatency_set_min_lat_nsec(blkg, lat_val);
+       if (enable) {
+               WARN_ON_ONCE(!blk_get_queue(blkg->q));
+               blkg_get(blkg);
+       }
+
        if (oldval != iolat->min_lat_nsec) {
                iolatency_clear_scaling(blkg);
        }
@@ -842,6 +858,24 @@ static ssize_t iolatency_set_limit(struct kernfs_open_file *of, char *buf,
        ret = 0;
 out:
        blkg_conf_finish(&ctx);
+       if (ret == 0 && enable) {
+               struct iolatency_grp *tmp = blkg_to_lat(blkg);
+               struct blk_iolatency *blkiolat = tmp->blkiolat;
+
+               blk_mq_freeze_queue(blkg->q);
+
+               if (enable == 1)
+                       atomic_inc(&blkiolat->enabled);
+               else if (enable == -1)
+                       atomic_dec(&blkiolat->enabled);
+               else
+                       WARN_ON_ONCE(1);
+
+               blk_mq_unfreeze_queue(blkg->q);
+
+               blkg_put(blkg);
+               blk_put_queue(blkg->q);
+       }
        return ret ?: nbytes;
 }
 
@@ -977,8 +1011,14 @@ static void iolatency_pd_offline(struct blkg_policy_data *pd)
 {
        struct iolatency_grp *iolat = pd_to_lat(pd);
        struct blkcg_gq *blkg = lat_to_blkg(iolat);
+       struct blk_iolatency *blkiolat = iolat->blkiolat;
+       int ret;
 
-       iolatency_set_min_lat_nsec(blkg, 0);
+       ret = iolatency_set_min_lat_nsec(blkg, 0);
+       if (ret == 1)
+               atomic_inc(&blkiolat->enabled);
+       if (ret == -1)
+               atomic_dec(&blkiolat->enabled);
        iolatency_clear_scaling(blkg);
 }
 
index d79a22f..71e9ac0 100644 (file)
@@ -272,6 +272,16 @@ void blk_queue_split(struct request_queue *q, struct bio **bio)
                /* there isn't chance to merge the splitted bio */
                split->bi_opf |= REQ_NOMERGE;
 
+               /*
+                * Since we're recursing into make_request here, ensure
+                * that we mark this bio as already having entered the queue.
+                * If not, and the queue is going away, we can get stuck
+                * forever on waiting for the queue reference to drop. But
+                * that will never happen, as we're already holding a
+                * reference to it.
+                */
+               bio_set_flag(*bio, BIO_QUEUE_ENTERED);
+
                bio_chain(split, *bio);
                trace_block_split(q, split, (*bio)->bi_iter.bi_sector);
                generic_make_request(*bio);
index f812083..7921573 100644 (file)
@@ -839,6 +839,9 @@ static const struct blk_mq_debugfs_attr blk_mq_debugfs_ctx_attrs[] = {
 static bool debugfs_create_files(struct dentry *parent, void *data,
                                 const struct blk_mq_debugfs_attr *attr)
 {
+       if (IS_ERR_OR_NULL(parent))
+               return false;
+
        d_inode(parent)->i_private = data;
 
        for (; attr->name; attr++) {
index 8f5b533..9437a5e 100644 (file)
@@ -737,12 +737,20 @@ static void blk_mq_requeue_work(struct work_struct *work)
        spin_unlock_irq(&q->requeue_lock);
 
        list_for_each_entry_safe(rq, next, &rq_list, queuelist) {
-               if (!(rq->rq_flags & RQF_SOFTBARRIER))
+               if (!(rq->rq_flags & (RQF_SOFTBARRIER | RQF_DONTPREP)))
                        continue;
 
                rq->rq_flags &= ~RQF_SOFTBARRIER;
                list_del_init(&rq->queuelist);
-               blk_mq_sched_insert_request(rq, true, false, false);
+               /*
+                * If RQF_DONTPREP, rq has contained some driver specific
+                * data, so insert it to hctx dispatch list to avoid any
+                * merge.
+                */
+               if (rq->rq_flags & RQF_DONTPREP)
+                       blk_mq_request_bypass_insert(rq, false);
+               else
+                       blk_mq_sched_insert_request(rq, true, false, false);
        }
 
        while (!list_empty(&rq_list)) {
index d943d46..d0b3dd5 100644 (file)
@@ -36,7 +36,6 @@ struct blk_mq_ctx {
        struct kobject          kobj;
 } ____cacheline_aligned_in_smp;
 
-void blk_mq_freeze_queue(struct request_queue *q);
 void blk_mq_free_queue(struct request_queue *q);
 int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr);
 void blk_mq_wake_waiters(struct request_queue *q);
index 5c093ce..147f6c7 100644 (file)
@@ -1029,6 +1029,9 @@ void __init acpi_early_init(void)
 
        acpi_permanent_mmap = true;
 
+       /* Initialize debug output. Linux does not use ACPICA defaults */
+       acpi_dbg_level = ACPI_LV_INFO | ACPI_LV_REPAIR;
+
 #ifdef CONFIG_X86
        /*
         * If the machine falls into the DMI check table,
index cdfc876..4d2b2ad 100644 (file)
@@ -5854,9 +5854,10 @@ static int __init init_binder_device(const char *name)
 static int __init binder_init(void)
 {
        int ret;
-       char *device_name, *device_names, *device_tmp;
+       char *device_name, *device_tmp;
        struct binder_device *device;
        struct hlist_node *tmp;
+       char *device_names = NULL;
 
        ret = binder_alloc_shrinker_init();
        if (ret)
@@ -5898,23 +5899,29 @@ static int __init binder_init(void)
                                    &transaction_log_fops);
        }
 
-       /*
-        * Copy the module_parameter string, because we don't want to
-        * tokenize it in-place.
-        */
-       device_names = kstrdup(binder_devices_param, GFP_KERNEL);
-       if (!device_names) {
-               ret = -ENOMEM;
-               goto err_alloc_device_names_failed;
-       }
+       if (strcmp(binder_devices_param, "") != 0) {
+               /*
+               * Copy the module_parameter string, because we don't want to
+               * tokenize it in-place.
+                */
+               device_names = kstrdup(binder_devices_param, GFP_KERNEL);
+               if (!device_names) {
+                       ret = -ENOMEM;
+                       goto err_alloc_device_names_failed;
+               }
 
-       device_tmp = device_names;
-       while ((device_name = strsep(&device_tmp, ","))) {
-               ret = init_binder_device(device_name);
-               if (ret)
-                       goto err_init_binder_device_failed;
+               device_tmp = device_names;
+               while ((device_name = strsep(&device_tmp, ","))) {
+                       ret = init_binder_device(device_name);
+                       if (ret)
+                               goto err_init_binder_device_failed;
+               }
        }
 
+       ret = init_binderfs();
+       if (ret)
+               goto err_init_binder_device_failed;
+
        return ret;
 
 err_init_binder_device_failed:
index 7fb97f5..045b3e4 100644 (file)
@@ -46,4 +46,13 @@ static inline bool is_binderfs_device(const struct inode *inode)
 }
 #endif
 
+#ifdef CONFIG_ANDROID_BINDERFS
+extern int __init init_binderfs(void);
+#else
+static inline int __init init_binderfs(void)
+{
+       return 0;
+}
+#endif
+
 #endif /* _LINUX_BINDER_INTERNAL_H */
index 6a2185e..e773f45 100644 (file)
@@ -395,6 +395,11 @@ static int binderfs_binder_ctl_create(struct super_block *sb)
        struct inode *inode = NULL;
        struct dentry *root = sb->s_root;
        struct binderfs_info *info = sb->s_fs_info;
+#if defined(CONFIG_IPC_NS)
+       bool use_reserve = (info->ipc_ns == &init_ipc_ns);
+#else
+       bool use_reserve = true;
+#endif
 
        device = kzalloc(sizeof(*device), GFP_KERNEL);
        if (!device)
@@ -413,7 +418,10 @@ static int binderfs_binder_ctl_create(struct super_block *sb)
 
        /* Reserve a new minor number for the new device. */
        mutex_lock(&binderfs_minors_mutex);
-       minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR, GFP_KERNEL);
+       minor = ida_alloc_max(&binderfs_minors,
+                             use_reserve ? BINDERFS_MAX_MINOR :
+                                           BINDERFS_MAX_MINOR_CAPPED,
+                             GFP_KERNEL);
        mutex_unlock(&binderfs_minors_mutex);
        if (minor < 0) {
                ret = minor;
@@ -542,7 +550,7 @@ static struct file_system_type binder_fs_type = {
        .fs_flags       = FS_USERNS_MOUNT,
 };
 
-static int __init init_binderfs(void)
+int __init init_binderfs(void)
 {
        int ret;
 
@@ -560,5 +568,3 @@ static int __init init_binderfs(void)
 
        return ret;
 }
-
-device_initcall(init_binderfs);
index b8c3f9e..adf2878 100644 (file)
@@ -4554,6 +4554,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
        { "SAMSUNG MZMPC128HBFU-000MV", "CXM14M1Q", ATA_HORKAGE_NOLPM, },
        { "SAMSUNG SSD PM830 mSATA *",  "CXM13D1Q", ATA_HORKAGE_NOLPM, },
        { "SAMSUNG MZ7TD256HAFV-000L9", NULL,       ATA_HORKAGE_NOLPM, },
+       { "SAMSUNG MZ7TE512HMHP-000L1", "EXT06L0Q", ATA_HORKAGE_NOLPM, },
 
        /* devices that don't properly handle queued TRIM commands */
        { "Micron_M500IT_*",            "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
index cf78fa6..a735953 100644 (file)
@@ -79,8 +79,7 @@ static void cache_size(struct cacheinfo *this_leaf, struct device_node *np)
        ct_idx = get_cacheinfo_idx(this_leaf->type);
        propname = cache_type_info[ct_idx].size_prop;
 
-       if (of_property_read_u32(np, propname, &this_leaf->size))
-               this_leaf->size = 0;
+       of_property_read_u32(np, propname, &this_leaf->size);
 }
 
 /* not cache_line_size() because that's a macro in include/linux/cache.h */
@@ -114,8 +113,7 @@ static void cache_nr_sets(struct cacheinfo *this_leaf, struct device_node *np)
        ct_idx = get_cacheinfo_idx(this_leaf->type);
        propname = cache_type_info[ct_idx].nr_sets_prop;
 
-       if (of_property_read_u32(np, propname, &this_leaf->number_of_sets))
-               this_leaf->number_of_sets = 0;
+       of_property_read_u32(np, propname, &this_leaf->number_of_sets);
 }
 
 static void cache_associativity(struct cacheinfo *this_leaf)
index 457be03..0ea2139 100644 (file)
@@ -130,7 +130,7 @@ u64 pm_runtime_autosuspend_expiration(struct device *dev)
 {
        int autosuspend_delay;
        u64 last_busy, expires = 0;
-       u64 now = ktime_to_ns(ktime_get());
+       u64 now = ktime_get_mono_fast_ns();
 
        if (!dev->power.use_autosuspend)
                goto out;
@@ -909,7 +909,7 @@ static enum hrtimer_restart  pm_suspend_timer_fn(struct hrtimer *timer)
         * If 'expires' is after the current time, we've been called
         * too early.
         */
-       if (expires > 0 && expires < ktime_to_ns(ktime_get())) {
+       if (expires > 0 && expires < ktime_get_mono_fast_ns()) {
                dev->power.timer_expires = 0;
                rpm_suspend(dev, dev->power.timer_autosuspends ?
                    (RPM_ASYNC | RPM_AUTO) : RPM_ASYNC);
@@ -928,7 +928,7 @@ static enum hrtimer_restart  pm_suspend_timer_fn(struct hrtimer *timer)
 int pm_schedule_suspend(struct device *dev, unsigned int delay)
 {
        unsigned long flags;
-       ktime_t expires;
+       u64 expires;
        int retval;
 
        spin_lock_irqsave(&dev->power.lock, flags);
@@ -945,8 +945,8 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay)
        /* Other scheduled or pending requests need to be canceled. */
        pm_runtime_cancel_pending(dev);
 
-       expires = ktime_add(ktime_get(), ms_to_ktime(delay));
-       dev->power.timer_expires = ktime_to_ns(expires);
+       expires = ktime_get_mono_fast_ns() + (u64)delay * NSEC_PER_MSEC;
+       dev->power.timer_expires = expires;
        dev->power.timer_autosuspends = 0;
        hrtimer_start(&dev->power.suspend_timer, expires, HRTIMER_MODE_ABS);
 
index a4aac37..6eded32 100644 (file)
 #include <linux/delay.h>
 
 #define bcma_err(bus, fmt, ...) \
-       pr_err("bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
+       dev_err((bus)->dev, "bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
 #define bcma_warn(bus, fmt, ...) \
-       pr_warn("bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
+       dev_warn((bus)->dev, "bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
 #define bcma_info(bus, fmt, ...) \
-       pr_info("bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
+       dev_info((bus)->dev, "bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
 #define bcma_debug(bus, fmt, ...) \
-       pr_debug("bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
+       dev_dbg((bus)->dev, "bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
 
 struct bcma_bus;
 
@@ -33,7 +33,6 @@ int __init bcma_bus_early_register(struct bcma_bus *bus);
 int bcma_bus_suspend(struct bcma_bus *bus);
 int bcma_bus_resume(struct bcma_bus *bus);
 #endif
-struct device *bcma_bus_get_host_dev(struct bcma_bus *bus);
 
 /* scan.c */
 void bcma_detect_chip(struct bcma_bus *bus);
index 2c0ffb7..a5df3d1 100644 (file)
@@ -183,7 +183,7 @@ int bcma_gpio_init(struct bcma_drv_cc *cc)
        chip->direction_input   = bcma_gpio_direction_input;
        chip->direction_output  = bcma_gpio_direction_output;
        chip->owner             = THIS_MODULE;
-       chip->parent            = bcma_bus_get_host_dev(bus);
+       chip->parent            = bus->dev;
 #if IS_BUILTIN(CONFIG_OF)
        chip->of_node           = cc->core->dev.of_node;
 #endif
index 63410ec..f52239f 100644 (file)
@@ -196,6 +196,8 @@ static int bcma_host_pci_probe(struct pci_dev *dev,
                goto err_pci_release_regions;
        }
 
+       bus->dev = &dev->dev;
+
        /* Map MMIO */
        err = -ENOMEM;
        bus->mmio = pci_iomap(dev, 0, ~0UL);
index 2dce347..c8073b5 100644 (file)
@@ -179,7 +179,6 @@ int __init bcma_host_soc_register(struct bcma_soc *soc)
        /* Host specific */
        bus->hosttype = BCMA_HOSTTYPE_SOC;
        bus->ops = &bcma_host_soc_ops;
-       bus->host_pdev = NULL;
 
        /* Initialize struct, detect chip */
        bcma_init_bus(bus);
@@ -213,6 +212,8 @@ static int bcma_host_soc_probe(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
+       bus->dev = dev;
+
        /* Map MMIO */
        bus->mmio = of_iomap(np, 0);
        if (!bus->mmio)
@@ -221,7 +222,6 @@ static int bcma_host_soc_probe(struct platform_device *pdev)
        /* Host specific */
        bus->hosttype = BCMA_HOSTTYPE_SOC;
        bus->ops = &bcma_host_soc_ops;
-       bus->host_pdev = pdev;
 
        /* Initialize struct, detect chip */
        bcma_init_bus(bus);
index fc1f4ac..6535614 100644 (file)
@@ -223,8 +223,8 @@ unsigned int bcma_core_irq(struct bcma_device *core, int num)
                        mips_irq = bcma_core_mips_irq(core);
                        return mips_irq <= 4 ? mips_irq + 2 : 0;
                }
-               if (bus->host_pdev)
-                       return bcma_of_get_irq(&bus->host_pdev->dev, core, num);
+               if (bus->dev)
+                       return bcma_of_get_irq(bus->dev, core, num);
                return 0;
        case BCMA_HOSTTYPE_SDIO:
                return 0;
@@ -239,18 +239,18 @@ void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core)
        core->dev.release = bcma_release_core_dev;
        core->dev.bus = &bcma_bus_type;
        dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
-       core->dev.parent = bcma_bus_get_host_dev(bus);
-       if (core->dev.parent)
-               bcma_of_fill_device(core->dev.parent, core);
+       core->dev.parent = bus->dev;
+       if (bus->dev)
+               bcma_of_fill_device(bus->dev, core);
 
        switch (bus->hosttype) {
        case BCMA_HOSTTYPE_PCI:
-               core->dma_dev = &bus->host_pci->dev;
+               core->dma_dev = bus->dev;
                core->irq = bus->host_pci->irq;
                break;
        case BCMA_HOSTTYPE_SOC:
-               if (IS_ENABLED(CONFIG_OF) && bus->host_pdev) {
-                       core->dma_dev = &bus->host_pdev->dev;
+               if (IS_ENABLED(CONFIG_OF) && bus->dev) {
+                       core->dma_dev = bus->dev;
                } else {
                        core->dev.dma_mask = &core->dev.coherent_dma_mask;
                        core->dma_dev = &core->dev;
@@ -261,28 +261,6 @@ void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core)
        }
 }
 
-struct device *bcma_bus_get_host_dev(struct bcma_bus *bus)
-{
-       switch (bus->hosttype) {
-       case BCMA_HOSTTYPE_PCI:
-               if (bus->host_pci)
-                       return &bus->host_pci->dev;
-               else
-                       return NULL;
-       case BCMA_HOSTTYPE_SOC:
-               if (bus->host_pdev)
-                       return &bus->host_pdev->dev;
-               else
-                       return NULL;
-       case BCMA_HOSTTYPE_SDIO:
-               if (bus->host_sdio)
-                       return &bus->host_sdio->dev;
-               else
-                       return NULL;
-       }
-       return NULL;
-}
-
 void bcma_init_bus(struct bcma_bus *bus)
 {
        mutex_lock(&bcma_buses_mutex);
@@ -402,7 +380,6 @@ int bcma_bus_register(struct bcma_bus *bus)
 {
        int err;
        struct bcma_device *core;
-       struct device *dev;
 
        /* Scan for devices (cores) */
        err = bcma_bus_scan(bus);
@@ -425,10 +402,8 @@ int bcma_bus_register(struct bcma_bus *bus)
                bcma_core_pci_early_init(&bus->drv_pci[0]);
        }
 
-       dev = bcma_bus_get_host_dev(bus);
-       if (dev) {
-               of_platform_default_populate(dev->of_node, NULL, dev);
-       }
+       if (bus->dev)
+               of_platform_default_populate(bus->dev->of_node, NULL, bus->dev);
 
        /* Cores providing flash access go before SPROM init */
        list_for_each_entry(core, &bus->cores, list) {
index 6f2856c..55481b4 100644 (file)
@@ -4075,7 +4075,7 @@ static unsigned int floppy_check_events(struct gendisk *disk,
 
        if (time_after(jiffies, UDRS->last_checked + UDP->checkfreq)) {
                if (lock_fdc(drive))
-                       return -EINTR;
+                       return 0;
                poll_drive(false, 0);
                process_fd_request();
        }
index 6ccdbed..d2477a5 100644 (file)
@@ -1513,9 +1513,19 @@ static int clk_fetch_parent_index(struct clk_core *core,
        if (!parent)
                return -EINVAL;
 
-       for (i = 0; i < core->num_parents; i++)
-               if (clk_core_get_parent_by_index(core, i) == parent)
+       for (i = 0; i < core->num_parents; i++) {
+               if (core->parents[i] == parent)
+                       return i;
+
+               if (core->parents[i])
+                       continue;
+
+               /* Fallback to comparing globally unique names */
+               if (!strcmp(parent->name, core->parent_names[i])) {
+                       core->parents[i] = parent;
                        return i;
+               }
+       }
 
        return -EINVAL;
 }
index 0026c39..76b9eb1 100644 (file)
@@ -155,13 +155,14 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 {
        struct clk_frac_pll *pll = to_clk_frac_pll(hw);
        u32 val, divfi, divff;
-       u64 temp64 = parent_rate;
+       u64 temp64;
        int ret;
 
        parent_rate *= 8;
        rate *= 2;
        divfi = rate / parent_rate;
-       temp64 *= rate - divfi;
+       temp64 = parent_rate * divfi;
+       temp64 = rate - temp64;
        temp64 *= PLL_FRAC_DENOM;
        do_div(temp64, parent_rate);
        divff = temp64;
index 61fefc0..d083b86 100644 (file)
@@ -53,7 +53,6 @@
 #define APMU_DISP1     0x110
 #define APMU_CCIC0     0x50
 #define APMU_CCIC1     0xf4
-#define APMU_SP                0x68
 #define MPMU_UART_PLL  0x14
 
 struct mmp2_clk_unit {
@@ -210,8 +209,6 @@ static struct mmp_clk_mix_config ccic1_mix_config = {
        .reg_info = DEFINE_MIX_REG_INFO(4, 16, 2, 6, 32),
 };
 
-static DEFINE_SPINLOCK(sp_lock);
-
 static struct mmp_param_mux_clk apmu_mux_clks[] = {
        {MMP2_CLK_DISP0_MUX, "disp0_mux", disp_parent_names, ARRAY_SIZE(disp_parent_names), CLK_SET_RATE_PARENT, APMU_DISP0, 6, 2, 0, &disp0_lock},
        {MMP2_CLK_DISP1_MUX, "disp1_mux", disp_parent_names, ARRAY_SIZE(disp_parent_names), CLK_SET_RATE_PARENT, APMU_DISP1, 6, 2, 0, &disp1_lock},
@@ -242,7 +239,6 @@ static struct mmp_param_gate_clk apmu_gate_clks[] = {
        {MMP2_CLK_CCIC1, "ccic1_clk", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x1b, 0x1b, 0x0, 0, &ccic1_lock},
        {MMP2_CLK_CCIC1_PHY, "ccic1_phy_clk", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x24, 0x24, 0x0, 0, &ccic1_lock},
        {MMP2_CLK_CCIC1_SPHY, "ccic1_sphy_clk", "ccic1_sphy_div", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x300, 0x300, 0x0, 0, &ccic1_lock},
-       {MMP2_CLK_SP, "sp_clk", NULL, CLK_SET_RATE_PARENT, APMU_SP, 0x1b, 0x1b, 0x0, 0, &sp_lock},
 };
 
 static void mmp2_axi_periph_clk_init(struct mmp2_clk_unit *pxa_unit)
index c782e62..58fa5c2 100644 (file)
@@ -115,8 +115,8 @@ static const char * const gcc_parent_names_6[] = {
        "core_bi_pll_test_se",
 };
 
-static const char * const gcc_parent_names_7[] = {
-       "bi_tcxo",
+static const char * const gcc_parent_names_7_ao[] = {
+       "bi_tcxo_ao",
        "gpll0",
        "gpll0_out_even",
        "core_bi_pll_test_se",
@@ -128,6 +128,12 @@ static const char * const gcc_parent_names_8[] = {
        "core_bi_pll_test_se",
 };
 
+static const char * const gcc_parent_names_8_ao[] = {
+       "bi_tcxo_ao",
+       "gpll0",
+       "core_bi_pll_test_se",
+};
+
 static const struct parent_map gcc_parent_map_10[] = {
        { P_BI_TCXO, 0 },
        { P_GPLL0_OUT_MAIN, 1 },
@@ -210,7 +216,7 @@ static struct clk_rcg2 gcc_cpuss_ahb_clk_src = {
        .freq_tbl = ftbl_gcc_cpuss_ahb_clk_src,
        .clkr.hw.init = &(struct clk_init_data){
                .name = "gcc_cpuss_ahb_clk_src",
-               .parent_names = gcc_parent_names_7,
+               .parent_names = gcc_parent_names_7_ao,
                .num_parents = 4,
                .ops = &clk_rcg2_ops,
        },
@@ -229,7 +235,7 @@ static struct clk_rcg2 gcc_cpuss_rbcpr_clk_src = {
        .freq_tbl = ftbl_gcc_cpuss_rbcpr_clk_src,
        .clkr.hw.init = &(struct clk_init_data){
                .name = "gcc_cpuss_rbcpr_clk_src",
-               .parent_names = gcc_parent_names_8,
+               .parent_names = gcc_parent_names_8_ao,
                .num_parents = 3,
                .ops = &clk_rcg2_ops,
        },
index 8d77090..0241450 100644 (file)
@@ -403,8 +403,10 @@ int ti_clk_parse_divider_data(int *div_table, int num_dividers, int max_div,
        num_dividers = i;
 
        tmp = kcalloc(valid_div + 1, sizeof(*tmp), GFP_KERNEL);
-       if (!tmp)
+       if (!tmp) {
+               *table = ERR_PTR(-ENOMEM);
                return -ENOMEM;
+       }
 
        valid_div = 0;
        *width = 0;
@@ -439,6 +441,7 @@ struct clk_hw *ti_clk_build_component_div(struct ti_clk_divider *setup)
 {
        struct clk_omap_divider *div;
        struct clk_omap_reg *reg;
+       int ret;
 
        if (!setup)
                return NULL;
@@ -458,6 +461,12 @@ struct clk_hw *ti_clk_build_component_div(struct ti_clk_divider *setup)
                div->flags |= CLK_DIVIDER_POWER_OF_TWO;
 
        div->table = _get_div_table_from_setup(setup, &div->width);
+       if (IS_ERR(div->table)) {
+               ret = PTR_ERR(div->table);
+               kfree(div);
+               return ERR_PTR(ret);
+       }
+
 
        div->shift = setup->bit_shift;
        div->latch = -EINVAL;
index b17d153..23a1b27 100644 (file)
@@ -21,7 +21,7 @@ static int __cpuidle poll_idle(struct cpuidle_device *dev,
        local_irq_enable();
        if (!current_set_polling_and_test()) {
                unsigned int loop_count = 0;
-               u64 limit = TICK_USEC;
+               u64 limit = TICK_NSEC;
                int i;
 
                for (i = 1; i < drv->state_count; i++) {
index fe070d7..4c97478 100644 (file)
@@ -537,6 +537,8 @@ static void process_response_list(struct nitrox_cmdq *cmdq)
        struct nitrox_device *ndev = cmdq->ndev;
        struct nitrox_softreq *sr;
        int req_completed = 0, err = 0, budget;
+       completion_t callback;
+       void *cb_arg;
 
        /* check all pending requests */
        budget = atomic_read(&cmdq->pending_count);
@@ -564,13 +566,13 @@ static void process_response_list(struct nitrox_cmdq *cmdq)
                smp_mb__after_atomic();
                /* remove from response list */
                response_list_del(sr, cmdq);
-
                /* ORH error code */
                err = READ_ONCE(*sr->resp.orh) & 0xff;
-
-               if (sr->callback)
-                       sr->callback(sr->cb_arg, err);
+               callback = sr->callback;
+               cb_arg = sr->cb_arg;
                softreq_destroy(sr);
+               if (callback)
+                       callback(cb_arg, err);
 
                req_completed++;
        }
index 8ada308..b0125ad 100644 (file)
@@ -380,7 +380,7 @@ static int init_cc_resources(struct platform_device *plat_dev)
        rc = cc_ivgen_init(new_drvdata);
        if (rc) {
                dev_err(dev, "cc_ivgen_init failed\n");
-               goto post_power_mgr_err;
+               goto post_buf_mgr_err;
        }
 
        /* Allocate crypto algs */
@@ -403,6 +403,9 @@ static int init_cc_resources(struct platform_device *plat_dev)
                goto post_hash_err;
        }
 
+       /* All set, we can allow autosuspend */
+       cc_pm_go(new_drvdata);
+
        /* If we got here and FIPS mode is enabled
         * it means all FIPS test passed, so let TEE
         * know we're good.
@@ -417,8 +420,6 @@ post_cipher_err:
        cc_cipher_free(new_drvdata);
 post_ivgen_err:
        cc_ivgen_fini(new_drvdata);
-post_power_mgr_err:
-       cc_pm_fini(new_drvdata);
 post_buf_mgr_err:
         cc_buffer_mgr_fini(new_drvdata);
 post_req_mgr_err:
index d990f47..6ff7e75 100644 (file)
@@ -100,20 +100,19 @@ int cc_pm_put_suspend(struct device *dev)
 
 int cc_pm_init(struct cc_drvdata *drvdata)
 {
-       int rc = 0;
        struct device *dev = drvdata_to_dev(drvdata);
 
        /* must be before the enabling to avoid resdundent suspending */
        pm_runtime_set_autosuspend_delay(dev, CC_SUSPEND_TIMEOUT);
        pm_runtime_use_autosuspend(dev);
        /* activate the PM module */
-       rc = pm_runtime_set_active(dev);
-       if (rc)
-               return rc;
-       /* enable the PM module*/
-       pm_runtime_enable(dev);
+       return pm_runtime_set_active(dev);
+}
 
-       return rc;
+/* enable the PM module*/
+void cc_pm_go(struct cc_drvdata *drvdata)
+{
+       pm_runtime_enable(drvdata_to_dev(drvdata));
 }
 
 void cc_pm_fini(struct cc_drvdata *drvdata)
index 020a540..f626243 100644 (file)
@@ -16,6 +16,7 @@
 extern const struct dev_pm_ops ccree_pm;
 
 int cc_pm_init(struct cc_drvdata *drvdata);
+void cc_pm_go(struct cc_drvdata *drvdata);
 void cc_pm_fini(struct cc_drvdata *drvdata);
 int cc_pm_suspend(struct device *dev);
 int cc_pm_resume(struct device *dev);
@@ -29,6 +30,8 @@ static inline int cc_pm_init(struct cc_drvdata *drvdata)
        return 0;
 }
 
+static void cc_pm_go(struct cc_drvdata *drvdata) {}
+
 static inline void cc_pm_fini(struct cc_drvdata *drvdata) {}
 
 static inline int cc_pm_suspend(struct device *dev)
index 4e55768..fe69dcc 100644 (file)
@@ -203,6 +203,7 @@ struct at_xdmac_chan {
        u32                             save_cim;
        u32                             save_cnda;
        u32                             save_cndc;
+       u32                             irq_status;
        unsigned long                   status;
        struct tasklet_struct           tasklet;
        struct dma_slave_config         sconfig;
@@ -1580,8 +1581,8 @@ static void at_xdmac_tasklet(unsigned long data)
        struct at_xdmac_desc    *desc;
        u32                     error_mask;
 
-       dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08lx\n",
-                __func__, atchan->status);
+       dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08x\n",
+               __func__, atchan->irq_status);
 
        error_mask = AT_XDMAC_CIS_RBEIS
                     | AT_XDMAC_CIS_WBEIS
@@ -1589,15 +1590,15 @@ static void at_xdmac_tasklet(unsigned long data)
 
        if (at_xdmac_chan_is_cyclic(atchan)) {
                at_xdmac_handle_cyclic(atchan);
-       } else if ((atchan->status & AT_XDMAC_CIS_LIS)
-                  || (atchan->status & error_mask)) {
+       } else if ((atchan->irq_status & AT_XDMAC_CIS_LIS)
+                  || (atchan->irq_status & error_mask)) {
                struct dma_async_tx_descriptor  *txd;
 
-               if (atchan->status & AT_XDMAC_CIS_RBEIS)
+               if (atchan->irq_status & AT_XDMAC_CIS_RBEIS)
                        dev_err(chan2dev(&atchan->chan), "read bus error!!!");
-               if (atchan->status & AT_XDMAC_CIS_WBEIS)
+               if (atchan->irq_status & AT_XDMAC_CIS_WBEIS)
                        dev_err(chan2dev(&atchan->chan), "write bus error!!!");
-               if (atchan->status & AT_XDMAC_CIS_ROIS)
+               if (atchan->irq_status & AT_XDMAC_CIS_ROIS)
                        dev_err(chan2dev(&atchan->chan), "request overflow error!!!");
 
                spin_lock(&atchan->lock);
@@ -1652,7 +1653,7 @@ static irqreturn_t at_xdmac_interrupt(int irq, void *dev_id)
                        atchan = &atxdmac->chan[i];
                        chan_imr = at_xdmac_chan_read(atchan, AT_XDMAC_CIM);
                        chan_status = at_xdmac_chan_read(atchan, AT_XDMAC_CIS);
-                       atchan->status = chan_status & chan_imr;
+                       atchan->irq_status = chan_status & chan_imr;
                        dev_vdbg(atxdmac->dma.dev,
                                 "%s: chan%d: imr=0x%x, status=0x%x\n",
                                 __func__, i, chan_imr, chan_status);
@@ -1666,7 +1667,7 @@ static irqreturn_t at_xdmac_interrupt(int irq, void *dev_id)
                                 at_xdmac_chan_read(atchan, AT_XDMAC_CDA),
                                 at_xdmac_chan_read(atchan, AT_XDMAC_CUBC));
 
-                       if (atchan->status & (AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS))
+                       if (atchan->irq_status & (AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS))
                                at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask);
 
                        tasklet_schedule(&atchan->tasklet);
index 1a44c80..ae10f56 100644 (file)
@@ -406,38 +406,32 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
        }
 }
 
-static int bcm2835_dma_abort(void __iomem *chan_base)
+static int bcm2835_dma_abort(struct bcm2835_chan *c)
 {
-       unsigned long cs;
+       void __iomem *chan_base = c->chan_base;
        long int timeout = 10000;
 
-       cs = readl(chan_base + BCM2835_DMA_CS);
-       if (!(cs & BCM2835_DMA_ACTIVE))
+       /*
+        * A zero control block address means the channel is idle.
+        * (The ACTIVE flag in the CS register is not a reliable indicator.)
+        */
+       if (!readl(chan_base + BCM2835_DMA_ADDR))
                return 0;
 
        /* Write 0 to the active bit - Pause the DMA */
        writel(0, chan_base + BCM2835_DMA_CS);
 
        /* Wait for any current AXI transfer to complete */
-       while ((cs & BCM2835_DMA_ISPAUSED) && --timeout) {
+       while ((readl(chan_base + BCM2835_DMA_CS) &
+               BCM2835_DMA_WAITING_FOR_WRITES) && --timeout)
                cpu_relax();
-               cs = readl(chan_base + BCM2835_DMA_CS);
-       }
 
-       /* We'll un-pause when we set of our next DMA */
+       /* Peripheral might be stuck and fail to signal AXI write responses */
        if (!timeout)
-               return -ETIMEDOUT;
-
-       if (!(cs & BCM2835_DMA_ACTIVE))
-               return 0;
-
-       /* Terminate the control block chain */
-       writel(0, chan_base + BCM2835_DMA_NEXTCB);
-
-       /* Abort the whole DMA */
-       writel(BCM2835_DMA_ABORT | BCM2835_DMA_ACTIVE,
-              chan_base + BCM2835_DMA_CS);
+               dev_err(c->vc.chan.device->dev,
+                       "failed to complete outstanding writes\n");
 
+       writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
        return 0;
 }
 
@@ -476,8 +470,15 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
 
        spin_lock_irqsave(&c->vc.lock, flags);
 
-       /* Acknowledge interrupt */
-       writel(BCM2835_DMA_INT, c->chan_base + BCM2835_DMA_CS);
+       /*
+        * Clear the INT flag to receive further interrupts. Keep the channel
+        * active in case the descriptor is cyclic or in case the client has
+        * already terminated the descriptor and issued a new one. (May happen
+        * if this IRQ handler is threaded.) If the channel is finished, it
+        * will remain idle despite the ACTIVE flag being set.
+        */
+       writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
+              c->chan_base + BCM2835_DMA_CS);
 
        d = c->desc;
 
@@ -485,11 +486,7 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
                if (d->cyclic) {
                        /* call the cyclic callback */
                        vchan_cyclic_callback(&d->vd);
-
-                       /* Keep the DMA engine running */
-                       writel(BCM2835_DMA_ACTIVE,
-                              c->chan_base + BCM2835_DMA_CS);
-               } else {
+               } else if (!readl(c->chan_base + BCM2835_DMA_ADDR)) {
                        vchan_cookie_complete(&c->desc->vd);
                        bcm2835_dma_start_desc(c);
                }
@@ -779,7 +776,6 @@ static int bcm2835_dma_terminate_all(struct dma_chan *chan)
        struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
        struct bcm2835_dmadev *d = to_bcm2835_dma_dev(c->vc.chan.device);
        unsigned long flags;
-       int timeout = 10000;
        LIST_HEAD(head);
 
        spin_lock_irqsave(&c->vc.lock, flags);
@@ -789,27 +785,11 @@ static int bcm2835_dma_terminate_all(struct dma_chan *chan)
        list_del_init(&c->node);
        spin_unlock(&d->lock);
 
-       /*
-        * Stop DMA activity: we assume the callback will not be called
-        * after bcm_dma_abort() returns (even if it does, it will see
-        * c->desc is NULL and exit.)
-        */
+       /* stop DMA activity */
        if (c->desc) {
                vchan_terminate_vdesc(&c->desc->vd);
                c->desc = NULL;
-               bcm2835_dma_abort(c->chan_base);
-
-               /* Wait for stopping */
-               while (--timeout) {
-                       if (!(readl(c->chan_base + BCM2835_DMA_CS) &
-                                               BCM2835_DMA_ACTIVE))
-                               break;
-
-                       cpu_relax();
-               }
-
-               if (!timeout)
-                       dev_err(d->ddev.dev, "DMA transfer could not be terminated\n");
+               bcm2835_dma_abort(c);
        }
 
        vchan_get_all_descriptors(&c->vc, &head);
index 2eea4ef..6511928 100644 (file)
@@ -711,11 +711,9 @@ static int dmatest_func(void *data)
                        srcs[i] = um->addr[i] + src_off;
                        ret = dma_mapping_error(dev->dev, um->addr[i]);
                        if (ret) {
-                               dmaengine_unmap_put(um);
                                result("src mapping error", total_tests,
                                       src_off, dst_off, len, ret);
-                               failed_tests++;
-                               continue;
+                               goto error_unmap_continue;
                        }
                        um->to_cnt++;
                }
@@ -730,11 +728,9 @@ static int dmatest_func(void *data)
                                               DMA_BIDIRECTIONAL);
                        ret = dma_mapping_error(dev->dev, dsts[i]);
                        if (ret) {
-                               dmaengine_unmap_put(um);
                                result("dst mapping error", total_tests,
                                       src_off, dst_off, len, ret);
-                               failed_tests++;
-                               continue;
+                               goto error_unmap_continue;
                        }
                        um->bidi_cnt++;
                }
@@ -762,12 +758,10 @@ static int dmatest_func(void *data)
                }
 
                if (!tx) {
-                       dmaengine_unmap_put(um);
                        result("prep error", total_tests, src_off,
                               dst_off, len, ret);
                        msleep(100);
-                       failed_tests++;
-                       continue;
+                       goto error_unmap_continue;
                }
 
                done->done = false;
@@ -776,12 +770,10 @@ static int dmatest_func(void *data)
                cookie = tx->tx_submit(tx);
 
                if (dma_submit_error(cookie)) {
-                       dmaengine_unmap_put(um);
                        result("submit error", total_tests, src_off,
                               dst_off, len, ret);
                        msleep(100);
-                       failed_tests++;
-                       continue;
+                       goto error_unmap_continue;
                }
                dma_async_issue_pending(chan);
 
@@ -790,22 +782,20 @@ static int dmatest_func(void *data)
 
                status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
 
-               dmaengine_unmap_put(um);
-
                if (!done->done) {
                        result("test timed out", total_tests, src_off, dst_off,
                               len, 0);
-                       failed_tests++;
-                       continue;
+                       goto error_unmap_continue;
                } else if (status != DMA_COMPLETE) {
                        result(status == DMA_ERROR ?
                               "completion error status" :
                               "completion busy status", total_tests, src_off,
                               dst_off, len, ret);
-                       failed_tests++;
-                       continue;
+                       goto error_unmap_continue;
                }
 
+               dmaengine_unmap_put(um);
+
                if (params->noverify) {
                        verbose_result("test passed", total_tests, src_off,
                                       dst_off, len, 0);
@@ -846,6 +836,12 @@ static int dmatest_func(void *data)
                        verbose_result("test passed", total_tests, src_off,
                                       dst_off, len, 0);
                }
+
+               continue;
+
+error_unmap_continue:
+               dmaengine_unmap_put(um);
+               failed_tests++;
        }
        ktime = ktime_sub(ktime_get(), ktime);
        ktime = ktime_sub(ktime, comparetime);
index c2fff3f..4a09af3 100644 (file)
@@ -618,7 +618,7 @@ static void imxdma_tasklet(unsigned long data)
 {
        struct imxdma_channel *imxdmac = (void *)data;
        struct imxdma_engine *imxdma = imxdmac->imxdma;
-       struct imxdma_desc *desc;
+       struct imxdma_desc *desc, *next_desc;
        unsigned long flags;
 
        spin_lock_irqsave(&imxdma->lock, flags);
@@ -648,10 +648,10 @@ static void imxdma_tasklet(unsigned long data)
        list_move_tail(imxdmac->ld_active.next, &imxdmac->ld_free);
 
        if (!list_empty(&imxdmac->ld_queue)) {
-               desc = list_first_entry(&imxdmac->ld_queue, struct imxdma_desc,
-                                       node);
+               next_desc = list_first_entry(&imxdmac->ld_queue,
+                                            struct imxdma_desc, node);
                list_move_tail(imxdmac->ld_queue.next, &imxdmac->ld_active);
-               if (imxdma_xfer_desc(desc) < 0)
+               if (imxdma_xfer_desc(next_desc) < 0)
                        dev_warn(imxdma->dev, "%s: channel: %d couldn't xfer desc\n",
                                 __func__, imxdmac->channel);
        }
index 4213cb0..f8664ba 100644 (file)
@@ -295,8 +295,8 @@ struct altr_sdram_mc_data {
 #define S10_SYSMGR_ECC_INTSTAT_DERR_OFST  0xA0
 
 /* Sticky registers for Uncorrected Errors */
-#define S10_SYSMGR_UE_VAL_OFST            0x120
-#define S10_SYSMGR_UE_ADDR_OFST           0x124
+#define S10_SYSMGR_UE_VAL_OFST            0x220
+#define S10_SYSMGR_UE_ADDR_OFST           0x224
 
 #define S10_DDR0_IRQ_MASK                 BIT(16)
 
index 472c88a..92f843e 100644 (file)
@@ -119,6 +119,11 @@ void scmi_driver_unregister(struct scmi_driver *driver)
 }
 EXPORT_SYMBOL_GPL(scmi_driver_unregister);
 
+static void scmi_device_release(struct device *dev)
+{
+       kfree(to_scmi_dev(dev));
+}
+
 struct scmi_device *
 scmi_device_create(struct device_node *np, struct device *parent, int protocol)
 {
@@ -138,6 +143,7 @@ scmi_device_create(struct device_node *np, struct device *parent, int protocol)
        scmi_dev->dev.parent = parent;
        scmi_dev->dev.of_node = np;
        scmi_dev->dev.bus = &scmi_bus_type;
+       scmi_dev->dev.release = scmi_device_release;
        dev_set_name(&scmi_dev->dev, "scmi_dev.%d", id);
 
        retval = device_register(&scmi_dev->dev);
@@ -156,9 +162,8 @@ free_mem:
 void scmi_device_destroy(struct scmi_device *scmi_dev)
 {
        scmi_handle_put(scmi_dev->handle);
-       device_unregister(&scmi_dev->dev);
        ida_simple_remove(&scmi_bus_id, scmi_dev->id);
-       kfree(scmi_dev);
+       device_unregister(&scmi_dev->dev);
 }
 
 void scmi_set_handle(struct scmi_device *scmi_dev)
index 23ea1ed..352bd24 100644 (file)
@@ -37,8 +37,9 @@ extern u64 efi_system_table;
 static struct ptdump_info efi_ptdump_info = {
        .mm             = &efi_mm,
        .markers        = (struct addr_marker[]){
-               { 0,            "UEFI runtime start" },
-               { DEFAULT_MAP_WINDOW_64, "UEFI runtime end" }
+               { 0,                            "UEFI runtime start" },
+               { DEFAULT_MAP_WINDOW_64,        "UEFI runtime end" },
+               { -1,                           NULL }
        },
        .base_addr      = 0,
 };
index a1a09e0..13851b3 100644 (file)
@@ -508,14 +508,11 @@ static int __init s10_init(void)
                return -ENODEV;
 
        np = of_find_matching_node(fw_np, s10_of_match);
-       if (!np) {
-               of_node_put(fw_np);
+       if (!np)
                return -ENODEV;
-       }
 
        of_node_put(np);
        ret = of_platform_populate(fw_np, s10_of_match, NULL, NULL);
-       of_node_put(fw_np);
        if (ret)
                return ret;
 
index 6b11f13..7f9e030 100644 (file)
@@ -66,8 +66,10 @@ static int altr_a10sr_gpio_direction_input(struct gpio_chip *gc,
 static int altr_a10sr_gpio_direction_output(struct gpio_chip *gc,
                                            unsigned int nr, int value)
 {
-       if (nr <= (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT))
+       if (nr <= (ALTR_A10SR_OUT_VALID_RANGE_HI - ALTR_A10SR_LED_VALID_SHIFT)) {
+               altr_a10sr_gpio_set(gc, nr, value);
                return 0;
+       }
        return -EINVAL;
 }
 
index e0d6a0a..e41223c 100644 (file)
@@ -180,7 +180,18 @@ static void sprd_eic_free(struct gpio_chip *chip, unsigned int offset)
 
 static int sprd_eic_get(struct gpio_chip *chip, unsigned int offset)
 {
-       return sprd_eic_read(chip, offset, SPRD_EIC_DBNC_DATA);
+       struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
+
+       switch (sprd_eic->type) {
+       case SPRD_EIC_DEBOUNCE:
+               return sprd_eic_read(chip, offset, SPRD_EIC_DBNC_DATA);
+       case SPRD_EIC_ASYNC:
+               return sprd_eic_read(chip, offset, SPRD_EIC_ASYNC_DATA);
+       case SPRD_EIC_SYNC:
+               return sprd_eic_read(chip, offset, SPRD_EIC_SYNC_DATA);
+       default:
+               return -ENOTSUPP;
+       }
 }
 
 static int sprd_eic_direction_input(struct gpio_chip *chip, unsigned int offset)
@@ -368,6 +379,7 @@ static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type)
                        irq_set_handler_locked(data, handle_edge_irq);
                        break;
                case IRQ_TYPE_EDGE_BOTH:
+                       sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 0);
                        sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 1);
                        irq_set_handler_locked(data, handle_edge_irq);
                        break;
index adf72dd..68a35b6 100644 (file)
@@ -84,6 +84,7 @@ MODULE_DEVICE_TABLE(of, pcf857x_of_table);
  */
 struct pcf857x {
        struct gpio_chip        chip;
+       struct irq_chip         irqchip;
        struct i2c_client       *client;
        struct mutex            lock;           /* protect 'out' */
        unsigned                out;            /* software latch */
@@ -252,18 +253,6 @@ static void pcf857x_irq_bus_sync_unlock(struct irq_data *data)
        mutex_unlock(&gpio->lock);
 }
 
-static struct irq_chip pcf857x_irq_chip = {
-       .name           = "pcf857x",
-       .irq_enable     = pcf857x_irq_enable,
-       .irq_disable    = pcf857x_irq_disable,
-       .irq_ack        = noop,
-       .irq_mask       = noop,
-       .irq_unmask     = noop,
-       .irq_set_wake   = pcf857x_irq_set_wake,
-       .irq_bus_lock           = pcf857x_irq_bus_lock,
-       .irq_bus_sync_unlock    = pcf857x_irq_bus_sync_unlock,
-};
-
 /*-------------------------------------------------------------------------*/
 
 static int pcf857x_probe(struct i2c_client *client,
@@ -376,8 +365,17 @@ static int pcf857x_probe(struct i2c_client *client,
 
        /* Enable irqchip if we have an interrupt */
        if (client->irq) {
+               gpio->irqchip.name = "pcf857x",
+               gpio->irqchip.irq_enable = pcf857x_irq_enable,
+               gpio->irqchip.irq_disable = pcf857x_irq_disable,
+               gpio->irqchip.irq_ack = noop,
+               gpio->irqchip.irq_mask = noop,
+               gpio->irqchip.irq_unmask = noop,
+               gpio->irqchip.irq_set_wake = pcf857x_irq_set_wake,
+               gpio->irqchip.irq_bus_lock = pcf857x_irq_bus_lock,
+               gpio->irqchip.irq_bus_sync_unlock = pcf857x_irq_bus_sync_unlock,
                status = gpiochip_irqchip_add_nested(&gpio->chip,
-                                                    &pcf857x_irq_chip,
+                                                    &gpio->irqchip,
                                                     0, handle_level_irq,
                                                     IRQ_TYPE_NONE);
                if (status) {
@@ -392,7 +390,7 @@ static int pcf857x_probe(struct i2c_client *client,
                if (status)
                        goto fail;
 
-               gpiochip_set_nested_irqchip(&gpio->chip, &pcf857x_irq_chip,
+               gpiochip_set_nested_irqchip(&gpio->chip, &gpio->irqchip,
                                            client->irq);
                gpio->irq_parent = client->irq;
        }
index 1b79ebc..541fa6a 100644 (file)
@@ -253,6 +253,7 @@ static int vf610_gpio_probe(struct platform_device *pdev)
        struct vf610_gpio_port *port;
        struct resource *iores;
        struct gpio_chip *gc;
+       int i;
        int ret;
 
        port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
@@ -319,6 +320,10 @@ static int vf610_gpio_probe(struct platform_device *pdev)
        if (ret < 0)
                return ret;
 
+       /* Mask all GPIO interrupts */
+       for (i = 0; i < gc->ngpio; i++)
+               vf610_gpio_writel(0, port->base + PORT_PCR(i));
+
        /* Clear the interrupt status register for all GPIO's */
        vf610_gpio_writel(~0, port->base + PORT_ISFR);
 
index 1651d7f..d1adfdf 100644 (file)
@@ -828,7 +828,14 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
        /* Do not leak kernel stack to userspace */
        memset(&ge, 0, sizeof(ge));
 
-       ge.timestamp = le->timestamp;
+       /*
+        * We may be running from a nested threaded interrupt in which case
+        * we didn't get the timestamp from lineevent_irq_handler().
+        */
+       if (!le->timestamp)
+               ge.timestamp = ktime_get_real_ns();
+       else
+               ge.timestamp = le->timestamp;
 
        if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE
            && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
index 6896dec..0ed41a9 100644 (file)
@@ -1686,7 +1686,8 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj,
                effective_mode &= ~S_IWUSR;
 
        if ((adev->flags & AMD_IS_APU) &&
-           (attr == &sensor_dev_attr_power1_cap_max.dev_attr.attr ||
+           (attr == &sensor_dev_attr_power1_average.dev_attr.attr ||
+            attr == &sensor_dev_attr_power1_cap_max.dev_attr.attr ||
             attr == &sensor_dev_attr_power1_cap_min.dev_attr.attr||
             attr == &sensor_dev_attr_power1_cap.dev_attr.attr))
                return 0;
index 71913a1..a38e0fb 100644 (file)
@@ -38,6 +38,7 @@
 #include "amdgpu_gem.h"
 #include <drm/amdgpu_drm.h>
 #include <linux/dma-buf.h>
+#include <linux/dma-fence-array.h>
 
 /**
  * amdgpu_gem_prime_get_sg_table - &drm_driver.gem_prime_get_sg_table
@@ -187,6 +188,48 @@ error:
        return ERR_PTR(ret);
 }
 
+static int
+__reservation_object_make_exclusive(struct reservation_object *obj)
+{
+       struct dma_fence **fences;
+       unsigned int count;
+       int r;
+
+       if (!reservation_object_get_list(obj)) /* no shared fences to convert */
+               return 0;
+
+       r = reservation_object_get_fences_rcu(obj, NULL, &count, &fences);
+       if (r)
+               return r;
+
+       if (count == 0) {
+               /* Now that was unexpected. */
+       } else if (count == 1) {
+               reservation_object_add_excl_fence(obj, fences[0]);
+               dma_fence_put(fences[0]);
+               kfree(fences);
+       } else {
+               struct dma_fence_array *array;
+
+               array = dma_fence_array_create(count, fences,
+                                              dma_fence_context_alloc(1), 0,
+                                              false);
+               if (!array)
+                       goto err_fences_put;
+
+               reservation_object_add_excl_fence(obj, &array->base);
+               dma_fence_put(&array->base);
+       }
+
+       return 0;
+
+err_fences_put:
+       while (count--)
+               dma_fence_put(fences[count]);
+       kfree(fences);
+       return -ENOMEM;
+}
+
 /**
  * amdgpu_gem_map_attach - &dma_buf_ops.attach implementation
  * @dma_buf: Shared DMA buffer
@@ -218,16 +261,16 @@ static int amdgpu_gem_map_attach(struct dma_buf *dma_buf,
 
        if (attach->dev->driver != adev->dev->driver) {
                /*
-                * Wait for all shared fences to complete before we switch to future
-                * use of exclusive fence on this prime shared bo.
+                * We only create shared fences for internal use, but importers
+                * of the dmabuf rely on exclusive fences for implicitly
+                * tracking write hazards. As any of the current fences may
+                * correspond to a write, we need to convert all existing
+                * fences on the reservation object into a single exclusive
+                * fence.
                 */
-               r = reservation_object_wait_timeout_rcu(bo->tbo.resv,
-                                                       true, false,
-                                                       MAX_SCHEDULE_TIMEOUT);
-               if (unlikely(r < 0)) {
-                       DRM_DEBUG_PRIME("Fence wait failed: %li\n", r);
+               r = __reservation_object_make_exclusive(bo->tbo.resv);
+               if (r)
                        goto error_unreserve;
-               }
        }
 
        /* pin buffer into GTT */
index 8fab0d6..3a9b48b 100644 (file)
@@ -90,8 +90,10 @@ static int psp_sw_fini(void *handle)
        adev->psp.sos_fw = NULL;
        release_firmware(adev->psp.asd_fw);
        adev->psp.asd_fw = NULL;
-       release_firmware(adev->psp.ta_fw);
-       adev->psp.ta_fw = NULL;
+       if (adev->psp.ta_fw) {
+               release_firmware(adev->psp.ta_fw);
+               adev->psp.ta_fw = NULL;
+       }
        return 0;
 }
 
@@ -435,6 +437,9 @@ static int psp_xgmi_initialize(struct psp_context *psp)
        struct ta_xgmi_shared_memory *xgmi_cmd;
        int ret;
 
+       if (!psp->adev->psp.ta_fw)
+               return -ENOENT;
+
        if (!psp->xgmi_context.initialized) {
                ret = psp_xgmi_init_shared_buf(psp);
                if (ret)
index d2ea5ce..7c108e6 100644 (file)
@@ -3363,14 +3363,15 @@ void amdgpu_vm_get_task_info(struct amdgpu_device *adev, unsigned int pasid,
                         struct amdgpu_task_info *task_info)
 {
        struct amdgpu_vm *vm;
+       unsigned long flags;
 
-       spin_lock(&adev->vm_manager.pasid_lock);
+       spin_lock_irqsave(&adev->vm_manager.pasid_lock, flags);
 
        vm = idr_find(&adev->vm_manager.pasid_idr, pasid);
        if (vm)
                *task_info = vm->task_info;
 
-       spin_unlock(&adev->vm_manager.pasid_lock);
+       spin_unlock_irqrestore(&adev->vm_manager.pasid_lock, flags);
 }
 
 /**
index 4cd31a2..186db18 100644 (file)
@@ -93,7 +93,20 @@ static void nbio_v7_4_enable_doorbell_aperture(struct amdgpu_device *adev,
 static void nbio_v7_4_enable_doorbell_selfring_aperture(struct amdgpu_device *adev,
                                                        bool enable)
 {
+       u32 tmp = 0;
 
+       if (enable) {
+               tmp = REG_SET_FIELD(tmp, DOORBELL_SELFRING_GPA_APER_CNTL, DOORBELL_SELFRING_GPA_APER_EN, 1) |
+                     REG_SET_FIELD(tmp, DOORBELL_SELFRING_GPA_APER_CNTL, DOORBELL_SELFRING_GPA_APER_MODE, 1) |
+                     REG_SET_FIELD(tmp, DOORBELL_SELFRING_GPA_APER_CNTL, DOORBELL_SELFRING_GPA_APER_SIZE, 0);
+
+               WREG32_SOC15(NBIO, 0, mmDOORBELL_SELFRING_GPA_APER_BASE_LOW,
+                            lower_32_bits(adev->doorbell.base));
+               WREG32_SOC15(NBIO, 0, mmDOORBELL_SELFRING_GPA_APER_BASE_HIGH,
+                            upper_32_bits(adev->doorbell.base));
+       }
+
+       WREG32_SOC15(NBIO, 0, mmDOORBELL_SELFRING_GPA_APER_CNTL, tmp);
 }
 
 static void nbio_v7_4_ih_doorbell_range(struct amdgpu_device *adev,
index 0c6e7f9..189fcb0 100644 (file)
@@ -152,18 +152,22 @@ static int psp_v11_0_init_microcode(struct psp_context *psp)
 
        snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_ta.bin", chip_name);
        err = request_firmware(&adev->psp.ta_fw, fw_name, adev->dev);
-       if (err)
-               goto out2;
-
-       err = amdgpu_ucode_validate(adev->psp.ta_fw);
-       if (err)
-               goto out2;
-
-       ta_hdr = (const struct ta_firmware_header_v1_0 *)adev->psp.ta_fw->data;
-       adev->psp.ta_xgmi_ucode_version = le32_to_cpu(ta_hdr->ta_xgmi_ucode_version);
-       adev->psp.ta_xgmi_ucode_size = le32_to_cpu(ta_hdr->ta_xgmi_size_bytes);
-       adev->psp.ta_xgmi_start_addr = (uint8_t *)ta_hdr +
-               le32_to_cpu(ta_hdr->header.ucode_array_offset_bytes);
+       if (err) {
+               release_firmware(adev->psp.ta_fw);
+               adev->psp.ta_fw = NULL;
+               dev_info(adev->dev,
+                        "psp v11.0: Failed to load firmware \"%s\"\n", fw_name);
+       } else {
+               err = amdgpu_ucode_validate(adev->psp.ta_fw);
+               if (err)
+                       goto out2;
+
+               ta_hdr = (const struct ta_firmware_header_v1_0 *)adev->psp.ta_fw->data;
+               adev->psp.ta_xgmi_ucode_version = le32_to_cpu(ta_hdr->ta_xgmi_ucode_version);
+               adev->psp.ta_xgmi_ucode_size = le32_to_cpu(ta_hdr->ta_xgmi_size_bytes);
+               adev->psp.ta_xgmi_start_addr = (uint8_t *)ta_hdr +
+                       le32_to_cpu(ta_hdr->header.ucode_array_offset_bytes);
+       }
 
        return 0;
 
index 8849b74..9b63997 100644 (file)
@@ -729,11 +729,13 @@ static int soc15_common_early_init(void *handle)
        case CHIP_RAVEN:
                adev->asic_funcs = &soc15_asic_funcs;
                if (adev->rev_id >= 0x8)
-                       adev->external_rev_id = adev->rev_id + 0x81;
+                       adev->external_rev_id = adev->rev_id + 0x79;
                else if (adev->pdev->device == 0x15d8)
                        adev->external_rev_id = adev->rev_id + 0x41;
+               else if (adev->rev_id == 1)
+                       adev->external_rev_id = adev->rev_id + 0x20;
                else
-                       adev->external_rev_id = 0x1;
+                       adev->external_rev_id = adev->rev_id + 0x01;
 
                if (adev->rev_id >= 0x8) {
                        adev->cg_flags = AMD_CG_SUPPORT_GFX_MGCG |
index 5d85ff3..2e7c449 100644 (file)
@@ -863,7 +863,7 @@ static int kfd_fill_mem_info_for_cpu(int numa_node_id, int *avail_size,
        return 0;
 }
 
-#if CONFIG_X86_64
+#ifdef CONFIG_X86_64
 static int kfd_fill_iolink_info_for_cpu(int numa_node_id, int *avail_size,
                                uint32_t *num_entries,
                                struct crat_subtype_iolink *sub_type_hdr)
index f4fa40c..0b392bf 100644 (file)
@@ -4082,7 +4082,8 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
        }
 
        if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
-           connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
+           connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
+           connector_type == DRM_MODE_CONNECTOR_eDP) {
                drm_connector_attach_vrr_capable_property(
                        &aconnector->base);
        }
index 9a7ac58..ddd75a4 100644 (file)
@@ -671,6 +671,25 @@ static ssize_t dp_phy_test_pattern_debugfs_write(struct file *f, const char __us
        return bytes_from_user;
 }
 
+/*
+ * Returns the min and max vrr vfreq through the connector's debugfs file.
+ * Example usage: cat /sys/kernel/debug/dri/0/DP-1/vrr_range
+ */
+static int vrr_range_show(struct seq_file *m, void *data)
+{
+       struct drm_connector *connector = m->private;
+       struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+
+       if (connector->status != connector_status_connected)
+               return -ENODEV;
+
+       seq_printf(m, "Min: %u\n", (unsigned int)aconnector->min_vfreq);
+       seq_printf(m, "Max: %u\n", (unsigned int)aconnector->max_vfreq);
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(vrr_range);
+
 static const struct file_operations dp_link_settings_debugfs_fops = {
        .owner = THIS_MODULE,
        .read = dp_link_settings_read,
@@ -697,7 +716,8 @@ static const struct {
 } dp_debugfs_entries[] = {
                {"link_settings", &dp_link_settings_debugfs_fops},
                {"phy_settings", &dp_phy_settings_debugfs_fop},
-               {"test_pattern", &dp_phy_test_pattern_fops}
+               {"test_pattern", &dp_phy_test_pattern_fops},
+               {"vrr_range", &vrr_range_fops}
 };
 
 int connector_debugfs_init(struct amdgpu_dm_connector *connector)
index afd287f..19801bd 100644 (file)
@@ -591,7 +591,15 @@ static void dce11_pplib_apply_display_requirements(
                        dc,
                        context->bw.dce.sclk_khz);
 
-       pp_display_cfg->min_dcfclock_khz = pp_display_cfg->min_engine_clock_khz;
+       /*
+        * As workaround for >4x4K lightup set dcfclock to min_engine_clock value.
+        * This is not required for less than 5 displays,
+        * thus don't request decfclk in dc to avoid impact
+        * on power saving.
+        *
+        */
+       pp_display_cfg->min_dcfclock_khz = (context->stream_count > 4)?
+                       pp_display_cfg->min_engine_clock_khz : 0;
 
        pp_display_cfg->min_engine_clock_deep_sleep_khz
                        = context->bw.dce.sclk_deep_sleep_khz;
index f95c5f5..5273de3 100644 (file)
@@ -1033,6 +1033,7 @@ static int smu10_get_clock_by_type_with_latency(struct pp_hwmgr *hwmgr,
                break;
        case amd_pp_dpp_clock:
                pclk_vol_table = pinfo->vdd_dep_on_dppclk;
+               break;
        default:
                return -EINVAL;
        }
index 99cba8e..5df1256 100644 (file)
@@ -528,7 +528,8 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev,
 
        object_count = cl->object_count;
 
-       object_ids = memdup_user(u64_to_user_ptr(cl->object_ids), object_count * sizeof(__u32));
+       object_ids = memdup_user(u64_to_user_ptr(cl->object_ids),
+                       array_size(object_count, sizeof(__u32)));
        if (IS_ERR(object_ids))
                return PTR_ERR(object_ids);
 
index 24a7504..f91e02c 100644 (file)
@@ -758,7 +758,7 @@ int drm_mode_hsync(const struct drm_display_mode *mode)
        if (mode->hsync)
                return mode->hsync;
 
-       if (mode->htotal < 0)
+       if (mode->htotal <= 0)
                return 0;
 
        calc_val = (mode->clock * 1000) / mode->htotal; /* hsync in Hz */
index 216f52b..c882ea9 100644 (file)
@@ -1824,6 +1824,16 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
        return 0;
 }
 
+static inline bool
+__vma_matches(struct vm_area_struct *vma, struct file *filp,
+             unsigned long addr, unsigned long size)
+{
+       if (vma->vm_file != filp)
+               return false;
+
+       return vma->vm_start == addr && (vma->vm_end - vma->vm_start) == size;
+}
+
 /**
  * i915_gem_mmap_ioctl - Maps the contents of an object, returning the address
  *                      it is mapped to.
@@ -1882,7 +1892,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
                        return -EINTR;
                }
                vma = find_vma(mm, addr);
-               if (vma)
+               if (vma && __vma_matches(vma, obj->base.filp, addr, args->size))
                        vma->vm_page_prot =
                                pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
                else
index d6c8f8f..017fc60 100644 (file)
@@ -594,7 +594,8 @@ static void i915_pmu_enable(struct perf_event *event)
         * Update the bitmask of enabled events and increment
         * the event reference counter.
         */
-       GEM_BUG_ON(bit >= I915_PMU_MASK_BITS);
+       BUILD_BUG_ON(ARRAY_SIZE(i915->pmu.enable_count) != I915_PMU_MASK_BITS);
+       GEM_BUG_ON(bit >= ARRAY_SIZE(i915->pmu.enable_count));
        GEM_BUG_ON(i915->pmu.enable_count[bit] == ~0);
        i915->pmu.enable |= BIT_ULL(bit);
        i915->pmu.enable_count[bit]++;
@@ -615,11 +616,16 @@ static void i915_pmu_enable(struct perf_event *event)
                engine = intel_engine_lookup_user(i915,
                                                  engine_event_class(event),
                                                  engine_event_instance(event));
-               GEM_BUG_ON(!engine);
-               engine->pmu.enable |= BIT(sample);
 
-               GEM_BUG_ON(sample >= I915_PMU_SAMPLE_BITS);
+               BUILD_BUG_ON(ARRAY_SIZE(engine->pmu.enable_count) !=
+                            I915_ENGINE_SAMPLE_COUNT);
+               BUILD_BUG_ON(ARRAY_SIZE(engine->pmu.sample) !=
+                            I915_ENGINE_SAMPLE_COUNT);
+               GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.enable_count));
+               GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.sample));
                GEM_BUG_ON(engine->pmu.enable_count[sample] == ~0);
+
+               engine->pmu.enable |= BIT(sample);
                engine->pmu.enable_count[sample]++;
        }
 
@@ -649,9 +655,11 @@ static void i915_pmu_disable(struct perf_event *event)
                engine = intel_engine_lookup_user(i915,
                                                  engine_event_class(event),
                                                  engine_event_instance(event));
-               GEM_BUG_ON(!engine);
-               GEM_BUG_ON(sample >= I915_PMU_SAMPLE_BITS);
+
+               GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.enable_count));
+               GEM_BUG_ON(sample >= ARRAY_SIZE(engine->pmu.sample));
                GEM_BUG_ON(engine->pmu.enable_count[sample] == 0);
+
                /*
                 * Decrement the reference count and clear the enabled
                 * bitmask when the last listener on an event goes away.
@@ -660,7 +668,7 @@ static void i915_pmu_disable(struct perf_event *event)
                        engine->pmu.enable &= ~BIT(sample);
        }
 
-       GEM_BUG_ON(bit >= I915_PMU_MASK_BITS);
+       GEM_BUG_ON(bit >= ARRAY_SIZE(i915->pmu.enable_count));
        GEM_BUG_ON(i915->pmu.enable_count[bit] == 0);
        /*
         * Decrement the reference count and clear the enabled
index 7f164ca..b3728c5 100644 (file)
@@ -31,6 +31,8 @@ enum {
        ((1 << I915_PMU_SAMPLE_BITS) + \
         (I915_PMU_LAST + 1 - __I915_PMU_OTHER(0)))
 
+#define I915_ENGINE_SAMPLE_COUNT (I915_SAMPLE_SEMA + 1)
+
 struct i915_pmu_sample {
        u64 cur;
 };
index 0a7d605..067054c 100644 (file)
@@ -1790,7 +1790,7 @@ enum i915_power_well_id {
 #define _CNL_PORT_TX_C_LN0_OFFSET              0x162C40
 #define _CNL_PORT_TX_D_LN0_OFFSET              0x162E40
 #define _CNL_PORT_TX_F_LN0_OFFSET              0x162840
-#define _CNL_PORT_TX_DW_GRP(port, dw)  (_PICK((port), \
+#define _CNL_PORT_TX_DW_GRP(dw, port)  (_PICK((port), \
                                               _CNL_PORT_TX_AE_GRP_OFFSET, \
                                               _CNL_PORT_TX_B_GRP_OFFSET, \
                                               _CNL_PORT_TX_B_GRP_OFFSET, \
@@ -1798,7 +1798,7 @@ enum i915_power_well_id {
                                               _CNL_PORT_TX_AE_GRP_OFFSET, \
                                               _CNL_PORT_TX_F_GRP_OFFSET) + \
                                               4 * (dw))
-#define _CNL_PORT_TX_DW_LN0(port, dw)  (_PICK((port), \
+#define _CNL_PORT_TX_DW_LN0(dw, port)  (_PICK((port), \
                                               _CNL_PORT_TX_AE_LN0_OFFSET, \
                                               _CNL_PORT_TX_B_LN0_OFFSET, \
                                               _CNL_PORT_TX_B_LN0_OFFSET, \
@@ -1834,9 +1834,9 @@ enum i915_power_well_id {
 
 #define _CNL_PORT_TX_DW4_LN0_AE                0x162450
 #define _CNL_PORT_TX_DW4_LN1_AE                0x1624D0
-#define CNL_PORT_TX_DW4_GRP(port)      _MMIO(_CNL_PORT_TX_DW_GRP((port), 4))
-#define CNL_PORT_TX_DW4_LN0(port)      _MMIO(_CNL_PORT_TX_DW_LN0((port), 4))
-#define CNL_PORT_TX_DW4_LN(port, ln)   _MMIO(_CNL_PORT_TX_DW_LN0((port), 4) + \
+#define CNL_PORT_TX_DW4_GRP(port)      _MMIO(_CNL_PORT_TX_DW_GRP(4, (port)))
+#define CNL_PORT_TX_DW4_LN0(port)      _MMIO(_CNL_PORT_TX_DW_LN0(4, (port)))
+#define CNL_PORT_TX_DW4_LN(port, ln)   _MMIO(_CNL_PORT_TX_DW_LN0(4, (port)) + \
                                           ((ln) * (_CNL_PORT_TX_DW4_LN1_AE - \
                                                    _CNL_PORT_TX_DW4_LN0_AE)))
 #define ICL_PORT_TX_DW4_AUX(port)      _MMIO(_ICL_PORT_TX_DW_AUX(4, port))
@@ -1864,8 +1864,12 @@ enum i915_power_well_id {
 #define   RTERM_SELECT(x)              ((x) << 3)
 #define   RTERM_SELECT_MASK            (0x7 << 3)
 
-#define CNL_PORT_TX_DW7_GRP(port)      _MMIO(_CNL_PORT_TX_DW_GRP((port), 7))
-#define CNL_PORT_TX_DW7_LN0(port)      _MMIO(_CNL_PORT_TX_DW_LN0((port), 7))
+#define CNL_PORT_TX_DW7_GRP(port)      _MMIO(_CNL_PORT_TX_DW_GRP(7, (port)))
+#define CNL_PORT_TX_DW7_LN0(port)      _MMIO(_CNL_PORT_TX_DW_LN0(7, (port)))
+#define ICL_PORT_TX_DW7_AUX(port)      _MMIO(_ICL_PORT_TX_DW_AUX(7, port))
+#define ICL_PORT_TX_DW7_GRP(port)      _MMIO(_ICL_PORT_TX_DW_GRP(7, port))
+#define ICL_PORT_TX_DW7_LN0(port)      _MMIO(_ICL_PORT_TX_DW_LN(7, 0, port))
+#define ICL_PORT_TX_DW7_LN(port, ln)   _MMIO(_ICL_PORT_TX_DW_LN(7, ln, port))
 #define   N_SCALAR(x)                  ((x) << 24)
 #define   N_SCALAR_MASK                        (0x7F << 24)
 
index f3e1d6a..7edce1b 100644 (file)
@@ -494,103 +494,58 @@ static const struct cnl_ddi_buf_trans cnl_ddi_translations_edp_1_05V[] = {
        { 0x2, 0x7F, 0x3F, 0x00, 0x00 },        /* 400   400      0.0   */
 };
 
-struct icl_combo_phy_ddi_buf_trans {
-       u32 dw2_swing_select;
-       u32 dw2_swing_scalar;
-       u32 dw4_scaling;
-};
-
-/* Voltage Swing Programming for VccIO 0.85V for DP */
-static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_dp_hdmi_0_85V[] = {
-                               /* Voltage mV  db    */
-       { 0x2, 0x98, 0x0018 },  /* 400         0.0   */
-       { 0x2, 0x98, 0x3015 },  /* 400         3.5   */
-       { 0x2, 0x98, 0x6012 },  /* 400         6.0   */
-       { 0x2, 0x98, 0x900F },  /* 400         9.5   */
-       { 0xB, 0x70, 0x0018 },  /* 600         0.0   */
-       { 0xB, 0x70, 0x3015 },  /* 600         3.5   */
-       { 0xB, 0x70, 0x6012 },  /* 600         6.0   */
-       { 0x5, 0x00, 0x0018 },  /* 800         0.0   */
-       { 0x5, 0x00, 0x3015 },  /* 800         3.5   */
-       { 0x6, 0x98, 0x0018 },  /* 1200        0.0   */
-};
-
-/* FIXME - After table is updated in Bspec */
-/* Voltage Swing Programming for VccIO 0.85V for eDP */
-static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_edp_0_85V[] = {
-                               /* Voltage mV  db    */
-       { 0x0, 0x00, 0x00 },    /* 200         0.0   */
-       { 0x0, 0x00, 0x00 },    /* 200         1.5   */
-       { 0x0, 0x00, 0x00 },    /* 200         4.0   */
-       { 0x0, 0x00, 0x00 },    /* 200         6.0   */
-       { 0x0, 0x00, 0x00 },    /* 250         0.0   */
-       { 0x0, 0x00, 0x00 },    /* 250         1.5   */
-       { 0x0, 0x00, 0x00 },    /* 250         4.0   */
-       { 0x0, 0x00, 0x00 },    /* 300         0.0   */
-       { 0x0, 0x00, 0x00 },    /* 300         1.5   */
-       { 0x0, 0x00, 0x00 },    /* 350         0.0   */
-};
-
-/* Voltage Swing Programming for VccIO 0.95V for DP */
-static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_dp_hdmi_0_95V[] = {
-                               /* Voltage mV  db    */
-       { 0x2, 0x98, 0x0018 },  /* 400         0.0   */
-       { 0x2, 0x98, 0x3015 },  /* 400         3.5   */
-       { 0x2, 0x98, 0x6012 },  /* 400         6.0   */
-       { 0x2, 0x98, 0x900F },  /* 400         9.5   */
-       { 0x4, 0x98, 0x0018 },  /* 600         0.0   */
-       { 0x4, 0x98, 0x3015 },  /* 600         3.5   */
-       { 0x4, 0x98, 0x6012 },  /* 600         6.0   */
-       { 0x5, 0x76, 0x0018 },  /* 800         0.0   */
-       { 0x5, 0x76, 0x3015 },  /* 800         3.5   */
-       { 0x6, 0x98, 0x0018 },  /* 1200        0.0   */
+/* icl_combo_phy_ddi_translations */
+static const struct cnl_ddi_buf_trans icl_combo_phy_ddi_translations_dp_hbr2[] = {
+                                               /* NT mV Trans mV db    */
+       { 0xA, 0x35, 0x3F, 0x00, 0x00 },        /* 350   350      0.0   */
+       { 0xA, 0x4F, 0x37, 0x00, 0x08 },        /* 350   500      3.1   */
+       { 0xC, 0x71, 0x2F, 0x00, 0x10 },        /* 350   700      6.0   */
+       { 0x6, 0x7F, 0x2B, 0x00, 0x14 },        /* 350   900      8.2   */
+       { 0xA, 0x4C, 0x3F, 0x00, 0x00 },        /* 500   500      0.0   */
+       { 0xC, 0x73, 0x34, 0x00, 0x0B },        /* 500   700      2.9   */
+       { 0x6, 0x7F, 0x2F, 0x00, 0x10 },        /* 500   900      5.1   */
+       { 0xC, 0x6C, 0x3C, 0x00, 0x03 },        /* 650   700      0.6   */
+       { 0x6, 0x7F, 0x35, 0x00, 0x0A },        /* 600   900      3.5   */
+       { 0x6, 0x7F, 0x3F, 0x00, 0x00 },        /* 900   900      0.0   */
 };
 
-/* FIXME - After table is updated in Bspec */
-/* Voltage Swing Programming for VccIO 0.95V for eDP */
-static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_edp_0_95V[] = {
-                               /* Voltage mV  db    */
-       { 0x0, 0x00, 0x00 },    /* 200         0.0   */
-       { 0x0, 0x00, 0x00 },    /* 200         1.5   */
-       { 0x0, 0x00, 0x00 },    /* 200         4.0   */
-       { 0x0, 0x00, 0x00 },    /* 200         6.0   */
-       { 0x0, 0x00, 0x00 },    /* 250         0.0   */
-       { 0x0, 0x00, 0x00 },    /* 250         1.5   */
-       { 0x0, 0x00, 0x00 },    /* 250         4.0   */
-       { 0x0, 0x00, 0x00 },    /* 300         0.0   */
-       { 0x0, 0x00, 0x00 },    /* 300         1.5   */
-       { 0x0, 0x00, 0x00 },    /* 350         0.0   */
+static const struct cnl_ddi_buf_trans icl_combo_phy_ddi_translations_edp_hbr2[] = {
+                                               /* NT mV Trans mV db    */
+       { 0x0, 0x7F, 0x3F, 0x00, 0x00 },        /* 200   200      0.0   */
+       { 0x8, 0x7F, 0x38, 0x00, 0x07 },        /* 200   250      1.9   */
+       { 0x1, 0x7F, 0x33, 0x00, 0x0C },        /* 200   300      3.5   */
+       { 0x9, 0x7F, 0x31, 0x00, 0x0E },        /* 200   350      4.9   */
+       { 0x8, 0x7F, 0x3F, 0x00, 0x00 },        /* 250   250      0.0   */
+       { 0x1, 0x7F, 0x38, 0x00, 0x07 },        /* 250   300      1.6   */
+       { 0x9, 0x7F, 0x35, 0x00, 0x0A },        /* 250   350      2.9   */
+       { 0x1, 0x7F, 0x3F, 0x00, 0x00 },        /* 300   300      0.0   */
+       { 0x9, 0x7F, 0x38, 0x00, 0x07 },        /* 300   350      1.3   */
+       { 0x9, 0x7F, 0x3F, 0x00, 0x00 },        /* 350   350      0.0   */
 };
 
-/* Voltage Swing Programming for VccIO 1.05V for DP */
-static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_dp_hdmi_1_05V[] = {
-                               /* Voltage mV  db    */
-       { 0x2, 0x98, 0x0018 },  /* 400         0.0   */
-       { 0x2, 0x98, 0x3015 },  /* 400         3.5   */
-       { 0x2, 0x98, 0x6012 },  /* 400         6.0   */
-       { 0x2, 0x98, 0x900F },  /* 400         9.5   */
-       { 0x4, 0x98, 0x0018 },  /* 600         0.0   */
-       { 0x4, 0x98, 0x3015 },  /* 600         3.5   */
-       { 0x4, 0x98, 0x6012 },  /* 600         6.0   */
-       { 0x5, 0x71, 0x0018 },  /* 800         0.0   */
-       { 0x5, 0x71, 0x3015 },  /* 800         3.5   */
-       { 0x6, 0x98, 0x0018 },  /* 1200        0.0   */
+static const struct cnl_ddi_buf_trans icl_combo_phy_ddi_translations_edp_hbr3[] = {
+                                               /* NT mV Trans mV db    */
+       { 0xA, 0x35, 0x3F, 0x00, 0x00 },        /* 350   350      0.0   */
+       { 0xA, 0x4F, 0x37, 0x00, 0x08 },        /* 350   500      3.1   */
+       { 0xC, 0x71, 0x2F, 0x00, 0x10 },        /* 350   700      6.0   */
+       { 0x6, 0x7F, 0x2B, 0x00, 0x14 },        /* 350   900      8.2   */
+       { 0xA, 0x4C, 0x3F, 0x00, 0x00 },        /* 500   500      0.0   */
+       { 0xC, 0x73, 0x34, 0x00, 0x0B },        /* 500   700      2.9   */
+       { 0x6, 0x7F, 0x2F, 0x00, 0x10 },        /* 500   900      5.1   */
+       { 0xC, 0x6C, 0x3C, 0x00, 0x03 },        /* 650   700      0.6   */
+       { 0x6, 0x7F, 0x35, 0x00, 0x0A },        /* 600   900      3.5   */
+       { 0x6, 0x7F, 0x3F, 0x00, 0x00 },        /* 900   900      0.0   */
 };
 
-/* FIXME - After table is updated in Bspec */
-/* Voltage Swing Programming for VccIO 1.05V for eDP */
-static const struct icl_combo_phy_ddi_buf_trans icl_combo_phy_ddi_translations_edp_1_05V[] = {
-                               /* Voltage mV  db    */
-       { 0x0, 0x00, 0x00 },    /* 200         0.0   */
-       { 0x0, 0x00, 0x00 },    /* 200         1.5   */
-       { 0x0, 0x00, 0x00 },    /* 200         4.0   */
-       { 0x0, 0x00, 0x00 },    /* 200         6.0   */
-       { 0x0, 0x00, 0x00 },    /* 250         0.0   */
-       { 0x0, 0x00, 0x00 },    /* 250         1.5   */
-       { 0x0, 0x00, 0x00 },    /* 250         4.0   */
-       { 0x0, 0x00, 0x00 },    /* 300         0.0   */
-       { 0x0, 0x00, 0x00 },    /* 300         1.5   */
-       { 0x0, 0x00, 0x00 },    /* 350         0.0   */
+static const struct cnl_ddi_buf_trans icl_combo_phy_ddi_translations_hdmi[] = {
+                                               /* NT mV Trans mV db    */
+       { 0xA, 0x60, 0x3F, 0x00, 0x00 },        /* 450   450      0.0   */
+       { 0xB, 0x73, 0x36, 0x00, 0x09 },        /* 450   650      3.2   */
+       { 0x6, 0x7F, 0x31, 0x00, 0x0E },        /* 450   850      5.5   */
+       { 0xB, 0x73, 0x3F, 0x00, 0x00 },        /* 650   650      0.0   ALS */
+       { 0x6, 0x7F, 0x37, 0x00, 0x08 },        /* 650   850      2.3   */
+       { 0x6, 0x7F, 0x3F, 0x00, 0x00 },        /* 850   850      0.0   */
+       { 0x6, 0x7F, 0x35, 0x00, 0x0A },        /* 600   850      3.0   */
 };
 
 struct icl_mg_phy_ddi_buf_trans {
@@ -871,43 +826,23 @@ cnl_get_buf_trans_edp(struct drm_i915_private *dev_priv, int *n_entries)
        }
 }
 
-static const struct icl_combo_phy_ddi_buf_trans *
+static const struct cnl_ddi_buf_trans *
 icl_get_combo_buf_trans(struct drm_i915_private *dev_priv, enum port port,
-                       int type, int *n_entries)
+                       int type, int rate, int *n_entries)
 {
-       u32 voltage = I915_READ(ICL_PORT_COMP_DW3(port)) & VOLTAGE_INFO_MASK;
-
-       if (type == INTEL_OUTPUT_EDP && dev_priv->vbt.edp.low_vswing) {
-               switch (voltage) {
-               case VOLTAGE_INFO_0_85V:
-                       *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_edp_0_85V);
-                       return icl_combo_phy_ddi_translations_edp_0_85V;
-               case VOLTAGE_INFO_0_95V:
-                       *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_edp_0_95V);
-                       return icl_combo_phy_ddi_translations_edp_0_95V;
-               case VOLTAGE_INFO_1_05V:
-                       *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_edp_1_05V);
-                       return icl_combo_phy_ddi_translations_edp_1_05V;
-               default:
-                       MISSING_CASE(voltage);
-                       return NULL;
-               }
-       } else {
-               switch (voltage) {
-               case VOLTAGE_INFO_0_85V:
-                       *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_dp_hdmi_0_85V);
-                       return icl_combo_phy_ddi_translations_dp_hdmi_0_85V;
-               case VOLTAGE_INFO_0_95V:
-                       *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_dp_hdmi_0_95V);
-                       return icl_combo_phy_ddi_translations_dp_hdmi_0_95V;
-               case VOLTAGE_INFO_1_05V:
-                       *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_dp_hdmi_1_05V);
-                       return icl_combo_phy_ddi_translations_dp_hdmi_1_05V;
-               default:
-                       MISSING_CASE(voltage);
-                       return NULL;
-               }
+       if (type == INTEL_OUTPUT_HDMI) {
+               *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_hdmi);
+               return icl_combo_phy_ddi_translations_hdmi;
+       } else if (rate > 540000 && type == INTEL_OUTPUT_EDP) {
+               *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_edp_hbr3);
+               return icl_combo_phy_ddi_translations_edp_hbr3;
+       } else if (type == INTEL_OUTPUT_EDP && dev_priv->vbt.edp.low_vswing) {
+               *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_edp_hbr2);
+               return icl_combo_phy_ddi_translations_edp_hbr2;
        }
+
+       *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_dp_hbr2);
+       return icl_combo_phy_ddi_translations_dp_hbr2;
 }
 
 static int intel_ddi_hdmi_level(struct drm_i915_private *dev_priv, enum port port)
@@ -918,8 +853,8 @@ static int intel_ddi_hdmi_level(struct drm_i915_private *dev_priv, enum port por
 
        if (IS_ICELAKE(dev_priv)) {
                if (intel_port_is_combophy(dev_priv, port))
-                       icl_get_combo_buf_trans(dev_priv, port,
-                                               INTEL_OUTPUT_HDMI, &n_entries);
+                       icl_get_combo_buf_trans(dev_priv, port, INTEL_OUTPUT_HDMI,
+                                               0, &n_entries);
                else
                        n_entries = ARRAY_SIZE(icl_mg_phy_ddi_translations);
                default_entry = n_entries - 1;
@@ -1086,7 +1021,7 @@ static uint32_t icl_pll_to_ddi_pll_sel(struct intel_encoder *encoder,
                        return DDI_CLK_SEL_TBT_810;
                default:
                        MISSING_CASE(clock);
-                       break;
+                       return DDI_CLK_SEL_NONE;
                }
        case DPLL_ID_ICL_MGPLL1:
        case DPLL_ID_ICL_MGPLL2:
@@ -2275,13 +2210,14 @@ static void bxt_ddi_vswing_sequence(struct intel_encoder *encoder,
 u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
        enum port port = encoder->port;
        int n_entries;
 
        if (IS_ICELAKE(dev_priv)) {
                if (intel_port_is_combophy(dev_priv, port))
                        icl_get_combo_buf_trans(dev_priv, port, encoder->type,
-                                               &n_entries);
+                                               intel_dp->link_rate, &n_entries);
                else
                        n_entries = ARRAY_SIZE(icl_mg_phy_ddi_translations);
        } else if (IS_CANNONLAKE(dev_priv)) {
@@ -2462,14 +2398,15 @@ static void cnl_ddi_vswing_sequence(struct intel_encoder *encoder,
 }
 
 static void icl_ddi_combo_vswing_program(struct drm_i915_private *dev_priv,
-                                        u32 level, enum port port, int type)
+                                       u32 level, enum port port, int type,
+                                       int rate)
 {
-       const struct icl_combo_phy_ddi_buf_trans *ddi_translations = NULL;
+       const struct cnl_ddi_buf_trans *ddi_translations = NULL;
        u32 n_entries, val;
        int ln;
 
        ddi_translations = icl_get_combo_buf_trans(dev_priv, port, type,
-                                                  &n_entries);
+                                                  rate, &n_entries);
        if (!ddi_translations)
                return;
 
@@ -2478,34 +2415,23 @@ static void icl_ddi_combo_vswing_program(struct drm_i915_private *dev_priv,
                level = n_entries - 1;
        }
 
-       /* Set PORT_TX_DW5 Rterm Sel to 110b. */
+       /* Set PORT_TX_DW5 */
        val = I915_READ(ICL_PORT_TX_DW5_LN0(port));
-       val &= ~RTERM_SELECT_MASK;
+       val &= ~(SCALING_MODE_SEL_MASK | RTERM_SELECT_MASK |
+                 TAP2_DISABLE | TAP3_DISABLE);
+       val |= SCALING_MODE_SEL(0x2);
        val |= RTERM_SELECT(0x6);
-       I915_WRITE(ICL_PORT_TX_DW5_GRP(port), val);
-
-       /* Program PORT_TX_DW5 */
-       val = I915_READ(ICL_PORT_TX_DW5_LN0(port));
-       /* Set DisableTap2 and DisableTap3 if MIPI DSI
-        * Clear DisableTap2 and DisableTap3 for all other Ports
-        */
-       if (type == INTEL_OUTPUT_DSI) {
-               val |= TAP2_DISABLE;
-               val |= TAP3_DISABLE;
-       } else {
-               val &= ~TAP2_DISABLE;
-               val &= ~TAP3_DISABLE;
-       }
+       val |= TAP3_DISABLE;
        I915_WRITE(ICL_PORT_TX_DW5_GRP(port), val);
 
        /* Program PORT_TX_DW2 */
        val = I915_READ(ICL_PORT_TX_DW2_LN0(port));
        val &= ~(SWING_SEL_LOWER_MASK | SWING_SEL_UPPER_MASK |
                 RCOMP_SCALAR_MASK);
-       val |= SWING_SEL_UPPER(ddi_translations[level].dw2_swing_select);
-       val |= SWING_SEL_LOWER(ddi_translations[level].dw2_swing_select);
+       val |= SWING_SEL_UPPER(ddi_translations[level].dw2_swing_sel);
+       val |= SWING_SEL_LOWER(ddi_translations[level].dw2_swing_sel);
        /* Program Rcomp scalar for every table entry */
-       val |= RCOMP_SCALAR(ddi_translations[level].dw2_swing_scalar);
+       val |= RCOMP_SCALAR(0x98);
        I915_WRITE(ICL_PORT_TX_DW2_GRP(port), val);
 
        /* Program PORT_TX_DW4 */
@@ -2514,9 +2440,17 @@ static void icl_ddi_combo_vswing_program(struct drm_i915_private *dev_priv,
                val = I915_READ(ICL_PORT_TX_DW4_LN(port, ln));
                val &= ~(POST_CURSOR_1_MASK | POST_CURSOR_2_MASK |
                         CURSOR_COEFF_MASK);
-               val |= ddi_translations[level].dw4_scaling;
+               val |= POST_CURSOR_1(ddi_translations[level].dw4_post_cursor_1);
+               val |= POST_CURSOR_2(ddi_translations[level].dw4_post_cursor_2);
+               val |= CURSOR_COEFF(ddi_translations[level].dw4_cursor_coeff);
                I915_WRITE(ICL_PORT_TX_DW4_LN(port, ln), val);
        }
+
+       /* Program PORT_TX_DW7 */
+       val = I915_READ(ICL_PORT_TX_DW7_LN0(port));
+       val &= ~N_SCALAR_MASK;
+       val |= N_SCALAR(ddi_translations[level].dw7_n_scalar);
+       I915_WRITE(ICL_PORT_TX_DW7_GRP(port), val);
 }
 
 static void icl_combo_phy_ddi_vswing_sequence(struct intel_encoder *encoder,
@@ -2581,7 +2515,7 @@ static void icl_combo_phy_ddi_vswing_sequence(struct intel_encoder *encoder,
        I915_WRITE(ICL_PORT_TX_DW5_GRP(port), val);
 
        /* 5. Program swing and de-emphasis */
-       icl_ddi_combo_vswing_program(dev_priv, level, port, type);
+       icl_ddi_combo_vswing_program(dev_priv, level, port, type, rate);
 
        /* 6. Set training enable to trigger update */
        val = I915_READ(ICL_PORT_TX_DW5_LN0(port));
index 3da9c0f..2481281 100644 (file)
@@ -15415,16 +15415,45 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc,
        }
 }
 
+static bool has_bogus_dpll_config(const struct intel_crtc_state *crtc_state)
+{
+       struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+
+       /*
+        * Some SNB BIOSen (eg. ASUS K53SV) are known to misprogram
+        * the hardware when a high res displays plugged in. DPLL P
+        * divider is zero, and the pipe timings are bonkers. We'll
+        * try to disable everything in that case.
+        *
+        * FIXME would be nice to be able to sanitize this state
+        * without several WARNs, but for now let's take the easy
+        * road.
+        */
+       return IS_GEN6(dev_priv) &&
+               crtc_state->base.active &&
+               crtc_state->shared_dpll &&
+               crtc_state->port_clock == 0;
+}
+
 static void intel_sanitize_encoder(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        struct intel_connector *connector;
+       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
+       struct intel_crtc_state *crtc_state = crtc ?
+               to_intel_crtc_state(crtc->base.state) : NULL;
 
        /* We need to check both for a crtc link (meaning that the
         * encoder is active and trying to read from a pipe) and the
         * pipe itself being active. */
-       bool has_active_crtc = encoder->base.crtc &&
-               to_intel_crtc(encoder->base.crtc)->active;
+       bool has_active_crtc = crtc_state &&
+               crtc_state->base.active;
+
+       if (crtc_state && has_bogus_dpll_config(crtc_state)) {
+               DRM_DEBUG_KMS("BIOS has misprogrammed the hardware. Disabling pipe %c\n",
+                             pipe_name(crtc->pipe));
+               has_active_crtc = false;
+       }
 
        connector = intel_encoder_find_connector(encoder);
        if (connector && !has_active_crtc) {
@@ -15435,16 +15464,25 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
                /* Connector is active, but has no active pipe. This is
                 * fallout from our resume register restoring. Disable
                 * the encoder manually again. */
-               if (encoder->base.crtc) {
-                       struct drm_crtc_state *crtc_state = encoder->base.crtc->state;
+               if (crtc_state) {
+                       struct drm_encoder *best_encoder;
 
                        DRM_DEBUG_KMS("[ENCODER:%d:%s] manually disabled\n",
                                      encoder->base.base.id,
                                      encoder->base.name);
+
+                       /* avoid oopsing in case the hooks consult best_encoder */
+                       best_encoder = connector->base.state->best_encoder;
+                       connector->base.state->best_encoder = &encoder->base;
+
                        if (encoder->disable)
-                               encoder->disable(encoder, to_intel_crtc_state(crtc_state), connector->base.state);
+                               encoder->disable(encoder, crtc_state,
+                                                connector->base.state);
                        if (encoder->post_disable)
-                               encoder->post_disable(encoder, to_intel_crtc_state(crtc_state), connector->base.state);
+                               encoder->post_disable(encoder, crtc_state,
+                                                     connector->base.state);
+
+                       connector->base.state->best_encoder = best_encoder;
                }
                encoder->base.crtc = NULL;
 
index fdd2cbc..22a7460 100644 (file)
@@ -304,9 +304,11 @@ static int cnl_max_source_rate(struct intel_dp *intel_dp)
 static int icl_max_source_rate(struct intel_dp *intel_dp)
 {
        struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+       struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev);
        enum port port = dig_port->base.port;
 
-       if (port == PORT_B)
+       if (intel_port_is_combophy(dev_priv, port) &&
+           !intel_dp_is_edp(intel_dp))
                return 540000;
 
        return 810000;
index f94a04b..e9ddeaf 100644 (file)
@@ -209,6 +209,16 @@ struct intel_fbdev {
        unsigned long vma_flags;
        async_cookie_t cookie;
        int preferred_bpp;
+
+       /* Whether or not fbdev hpd processing is temporarily suspended */
+       bool hpd_suspended : 1;
+       /* Set when a hotplug was received while HPD processing was
+        * suspended
+        */
+       bool hpd_waiting : 1;
+
+       /* Protects hpd_suspended */
+       struct mutex hpd_lock;
 };
 
 struct intel_encoder {
index fb5bb5b..7f365ac 100644 (file)
@@ -679,6 +679,7 @@ int intel_fbdev_init(struct drm_device *dev)
        if (ifbdev == NULL)
                return -ENOMEM;
 
+       mutex_init(&ifbdev->hpd_lock);
        drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs);
 
        if (!intel_fbdev_init_bios(dev, ifbdev))
@@ -752,6 +753,26 @@ void intel_fbdev_fini(struct drm_i915_private *dev_priv)
        intel_fbdev_destroy(ifbdev);
 }
 
+/* Suspends/resumes fbdev processing of incoming HPD events. When resuming HPD
+ * processing, fbdev will perform a full connector reprobe if a hotplug event
+ * was received while HPD was suspended.
+ */
+static void intel_fbdev_hpd_set_suspend(struct intel_fbdev *ifbdev, int state)
+{
+       bool send_hpd = false;
+
+       mutex_lock(&ifbdev->hpd_lock);
+       ifbdev->hpd_suspended = state == FBINFO_STATE_SUSPENDED;
+       send_hpd = !ifbdev->hpd_suspended && ifbdev->hpd_waiting;
+       ifbdev->hpd_waiting = false;
+       mutex_unlock(&ifbdev->hpd_lock);
+
+       if (send_hpd) {
+               DRM_DEBUG_KMS("Handling delayed fbcon HPD event\n");
+               drm_fb_helper_hotplug_event(&ifbdev->helper);
+       }
+}
+
 void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -773,6 +794,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous
                 */
                if (state != FBINFO_STATE_RUNNING)
                        flush_work(&dev_priv->fbdev_suspend_work);
+
                console_lock();
        } else {
                /*
@@ -800,17 +822,26 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous
 
        drm_fb_helper_set_suspend(&ifbdev->helper, state);
        console_unlock();
+
+       intel_fbdev_hpd_set_suspend(ifbdev, state);
 }
 
 void intel_fbdev_output_poll_changed(struct drm_device *dev)
 {
        struct intel_fbdev *ifbdev = to_i915(dev)->fbdev;
+       bool send_hpd;
 
        if (!ifbdev)
                return;
 
        intel_fbdev_sync(ifbdev);
-       if (ifbdev->vma || ifbdev->helper.deferred_setup)
+
+       mutex_lock(&ifbdev->hpd_lock);
+       send_hpd = !ifbdev->hpd_suspended;
+       ifbdev->hpd_waiting = true;
+       mutex_unlock(&ifbdev->hpd_lock);
+
+       if (send_hpd && (ifbdev->vma || ifbdev->helper.deferred_setup))
                drm_fb_helper_hotplug_event(&ifbdev->helper);
 }
 
index b8f106d..3ac2015 100644 (file)
 struct opregion_header {
        u8 signature[16];
        u32 size;
-       u32 opregion_ver;
+       struct {
+               u8 rsvd;
+               u8 revision;
+               u8 minor;
+               u8 major;
+       }  __packed over;
        u8 bios_ver[32];
        u8 vbios_ver[16];
        u8 driver_ver[16];
@@ -119,7 +124,8 @@ struct opregion_asle {
        u64 fdss;
        u32 fdsp;
        u32 stat;
-       u64 rvda;       /* Physical address of raw vbt data */
+       u64 rvda;       /* Physical (2.0) or relative from opregion (2.1+)
+                        * address of raw VBT data. */
        u32 rvds;       /* Size of raw vbt data */
        u8 rsvd[58];
 } __packed;
@@ -925,6 +931,11 @@ int intel_opregion_setup(struct drm_i915_private *dev_priv)
        opregion->header = base;
        opregion->lid_state = base + ACPI_CLID;
 
+       DRM_DEBUG_DRIVER("ACPI OpRegion version %u.%u.%u\n",
+                        opregion->header->over.major,
+                        opregion->header->over.minor,
+                        opregion->header->over.revision);
+
        mboxes = opregion->header->mboxes;
        if (mboxes & MBOX_ACPI) {
                DRM_DEBUG_DRIVER("Public ACPI methods supported\n");
@@ -953,11 +964,26 @@ int intel_opregion_setup(struct drm_i915_private *dev_priv)
        if (dmi_check_system(intel_no_opregion_vbt))
                goto out;
 
-       if (opregion->header->opregion_ver >= 2 && opregion->asle &&
+       if (opregion->header->over.major >= 2 && opregion->asle &&
            opregion->asle->rvda && opregion->asle->rvds) {
-               opregion->rvda = memremap(opregion->asle->rvda,
-                                         opregion->asle->rvds,
+               resource_size_t rvda = opregion->asle->rvda;
+
+               /*
+                * opregion 2.0: rvda is the physical VBT address.
+                *
+                * opregion 2.1+: rvda is unsigned, relative offset from
+                * opregion base, and should never point within opregion.
+                */
+               if (opregion->header->over.major > 2 ||
+                   opregion->header->over.minor >= 1) {
+                       WARN_ON(rvda < OPREGION_SIZE);
+
+                       rvda += asls;
+               }
+
+               opregion->rvda = memremap(rvda, opregion->asle->rvds,
                                          MEMREMAP_WB);
+
                vbt = opregion->rvda;
                vbt_size = opregion->asle->rvds;
                if (intel_bios_is_valid_vbt(vbt, vbt_size)) {
@@ -967,6 +993,8 @@ int intel_opregion_setup(struct drm_i915_private *dev_priv)
                        goto out;
                } else {
                        DRM_DEBUG_KMS("Invalid VBT in ACPI OpRegion (RVDA)\n");
+                       memunmap(opregion->rvda);
+                       opregion->rvda = NULL;
                }
        }
 
index 72edaa7..a1a7cc2 100644 (file)
@@ -415,16 +415,17 @@ struct intel_engine_cs {
                /**
                 * @enable_count: Reference count for the enabled samplers.
                 *
-                * Index number corresponds to the bit number from @enable.
+                * Index number corresponds to @enum drm_i915_pmu_engine_sample.
                 */
-               unsigned int enable_count[I915_PMU_SAMPLE_BITS];
+               unsigned int enable_count[I915_ENGINE_SAMPLE_COUNT];
                /**
                 * @sample: Counter values for sampling events.
                 *
                 * Our internal timer stores the current counters in this field.
+                *
+                * Index number corresponds to @enum drm_i915_pmu_engine_sample.
                 */
-#define I915_ENGINE_SAMPLE_MAX (I915_SAMPLE_SEMA + 1)
-               struct i915_pmu_sample sample[I915_ENGINE_SAMPLE_MAX];
+               struct i915_pmu_sample sample[I915_ENGINE_SAMPLE_COUNT];
        } pmu;
 
        /*
index d2e003d..5170a0f 100644 (file)
@@ -494,7 +494,7 @@ skl_program_plane(struct intel_plane *plane,
 
        keymax = (key->max_value & 0xffffff) | PLANE_KEYMAX_ALPHA(alpha);
 
-       keymsk = key->channel_mask & 0x3ffffff;
+       keymsk = key->channel_mask & 0x7ffffff;
        if (alpha < 0xff)
                keymsk |= PLANE_KEYMSK_ALPHA_ENABLE;
 
index 2c5bbe3..e31e263 100644 (file)
@@ -643,8 +643,10 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
                int bus_format;
 
                ret = of_property_read_u32(child, "reg", &i);
-               if (ret || i < 0 || i > 1)
-                       return -EINVAL;
+               if (ret || i < 0 || i > 1) {
+                       ret = -EINVAL;
+                       goto free_child;
+               }
 
                if (!of_device_is_available(child))
                        continue;
@@ -657,7 +659,6 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
                channel = &imx_ldb->channel[i];
                channel->ldb = imx_ldb;
                channel->chno = i;
-               channel->child = child;
 
                /*
                 * The output port is port@4 with an external 4-port mux or
@@ -667,13 +668,13 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
                                                  imx_ldb->lvds_mux ? 4 : 2, 0,
                                                  &channel->panel, &channel->bridge);
                if (ret && ret != -ENODEV)
-                       return ret;
+                       goto free_child;
 
                /* panel ddc only if there is no bridge */
                if (!channel->bridge) {
                        ret = imx_ldb_panel_ddc(dev, channel, child);
                        if (ret)
-                               return ret;
+                               goto free_child;
                }
 
                bus_format = of_get_bus_format(dev, child);
@@ -689,18 +690,26 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
                if (bus_format < 0) {
                        dev_err(dev, "could not determine data mapping: %d\n",
                                bus_format);
-                       return bus_format;
+                       ret = bus_format;
+                       goto free_child;
                }
                channel->bus_format = bus_format;
+               channel->child = child;
 
                ret = imx_ldb_register(drm, channel);
-               if (ret)
-                       return ret;
+               if (ret) {
+                       channel->child = NULL;
+                       goto free_child;
+               }
        }
 
        dev_set_drvdata(dev, imx_ldb);
 
        return 0;
+
+free_child:
+       of_node_put(child);
+       return ret;
 }
 
 static void imx_ldb_unbind(struct device *dev, struct device *master,
index c390924..21e964f 100644 (file)
@@ -370,9 +370,9 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
        if (ret)
                return ret;
 
-       /* CRTC should be enabled */
+       /* nothing to check when disabling or disabled */
        if (!crtc_state->enable)
-               return -EINVAL;
+               return 0;
 
        switch (plane->type) {
        case DRM_PLANE_TYPE_PRIMARY:
index 00a9c2a..64fb788 100644 (file)
@@ -1406,7 +1406,7 @@ static void dsi_pll_disable(struct dss_pll *pll)
 
 static int dsi_dump_dsi_clocks(struct seq_file *s, void *p)
 {
-       struct dsi_data *dsi = p;
+       struct dsi_data *dsi = s->private;
        struct dss_pll_clock_info *cinfo = &dsi->pll.cinfo;
        enum dss_clk_source dispc_clk_src, dsi_clk_src;
        int dsi_module = dsi->module_id;
@@ -1467,7 +1467,7 @@ static int dsi_dump_dsi_clocks(struct seq_file *s, void *p)
 #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
 static int dsi_dump_dsi_irqs(struct seq_file *s, void *p)
 {
-       struct dsi_data *dsi = p;
+       struct dsi_data *dsi = s->private;
        unsigned long flags;
        struct dsi_irq_stats stats;
 
@@ -1558,7 +1558,7 @@ static int dsi_dump_dsi_irqs(struct seq_file *s, void *p)
 
 static int dsi_dump_dsi_regs(struct seq_file *s, void *p)
 {
-       struct dsi_data *dsi = p;
+       struct dsi_data *dsi = s->private;
 
        if (dsi_runtime_get(dsi))
                return 0;
@@ -4751,6 +4751,17 @@ static int dsi_set_config(struct omap_dss_device *dssdev,
        dsi->vm.flags |= DISPLAY_FLAGS_HSYNC_HIGH;
        dsi->vm.flags &= ~DISPLAY_FLAGS_VSYNC_LOW;
        dsi->vm.flags |= DISPLAY_FLAGS_VSYNC_HIGH;
+       /*
+        * HACK: These flags should be handled through the omap_dss_device bus
+        * flags, but this will only be possible when the DSI encoder will be
+        * converted to the omapdrm-managed encoder model.
+        */
+       dsi->vm.flags &= ~DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+       dsi->vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
+       dsi->vm.flags &= ~DISPLAY_FLAGS_DE_LOW;
+       dsi->vm.flags |= DISPLAY_FLAGS_DE_HIGH;
+       dsi->vm.flags &= ~DISPLAY_FLAGS_SYNC_POSEDGE;
+       dsi->vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
 
        dss_mgr_set_timings(&dsi->output, &dsi->vm);
 
@@ -5083,15 +5094,15 @@ static int dsi_bind(struct device *dev, struct device *master, void *data)
 
        snprintf(name, sizeof(name), "dsi%u_regs", dsi->module_id + 1);
        dsi->debugfs.regs = dss_debugfs_create_file(dss, name,
-                                                   dsi_dump_dsi_regs, &dsi);
+                                                   dsi_dump_dsi_regs, dsi);
 #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
        snprintf(name, sizeof(name), "dsi%u_irqs", dsi->module_id + 1);
        dsi->debugfs.irqs = dss_debugfs_create_file(dss, name,
-                                                   dsi_dump_dsi_irqs, &dsi);
+                                                   dsi_dump_dsi_irqs, dsi);
 #endif
        snprintf(name, sizeof(name), "dsi%u_clks", dsi->module_id + 1);
        dsi->debugfs.clks = dss_debugfs_create_file(dss, name,
-                                                   dsi_dump_dsi_clocks, &dsi);
+                                                   dsi_dump_dsi_clocks, dsi);
 
        return 0;
 }
@@ -5104,8 +5115,6 @@ static void dsi_unbind(struct device *dev, struct device *master, void *data)
        dss_debugfs_remove_file(dsi->debugfs.irqs);
        dss_debugfs_remove_file(dsi->debugfs.regs);
 
-       of_platform_depopulate(dev);
-
        WARN_ON(dsi->scp_clk_refcount > 0);
 
        dss_pll_unregister(&dsi->pll);
@@ -5457,6 +5466,8 @@ static int dsi_remove(struct platform_device *pdev)
 
        dsi_uninit_output(dsi);
 
+       of_platform_depopulate(&pdev->dev);
+
        pm_runtime_disable(&pdev->dev);
 
        if (dsi->vdds_dsi_reg != NULL && dsi->vdds_dsi_enabled) {
index d587779..a97294a 100644 (file)
@@ -5676,7 +5676,7 @@ int ci_dpm_init(struct radeon_device *rdev)
        u16 data_offset, size;
        u8 frev, crev;
        struct ci_power_info *pi;
-       enum pci_bus_speed speed_cap;
+       enum pci_bus_speed speed_cap = PCI_SPEED_UNKNOWN;
        struct pci_dev *root = rdev->pdev->bus->self;
        int ret;
 
@@ -5685,7 +5685,8 @@ int ci_dpm_init(struct radeon_device *rdev)
                return -ENOMEM;
        rdev->pm.dpm.priv = pi;
 
-       speed_cap = pcie_get_speed_cap(root);
+       if (!pci_is_root_bus(rdev->pdev->bus))
+               speed_cap = pcie_get_speed_cap(root);
        if (speed_cap == PCI_SPEED_UNKNOWN) {
                pi->sys_pcie_mask = 0;
        } else {
index 8fb60b3..0a785ef 100644 (file)
@@ -6899,7 +6899,7 @@ int si_dpm_init(struct radeon_device *rdev)
        struct ni_power_info *ni_pi;
        struct si_power_info *si_pi;
        struct atom_clock_dividers dividers;
-       enum pci_bus_speed speed_cap;
+       enum pci_bus_speed speed_cap = PCI_SPEED_UNKNOWN;
        struct pci_dev *root = rdev->pdev->bus->self;
        int ret;
 
@@ -6911,7 +6911,8 @@ int si_dpm_init(struct radeon_device *rdev)
        eg_pi = &ni_pi->eg;
        pi = &eg_pi->rv7xx;
 
-       speed_cap = pcie_get_speed_cap(root);
+       if (!pci_is_root_bus(rdev->pdev->bus))
+               speed_cap = pcie_get_speed_cap(root);
        if (speed_cap == PCI_SPEED_UNKNOWN) {
                si_pi->sys_pcie_mask = 0;
        } else {
index 37f9302..c0351ab 100644 (file)
@@ -1,17 +1,8 @@
-//SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
  * Author:
  *      Sandy Huang <hjc@rock-chips.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  */
 
 #include <drm/drmP.h>
index 38b52e6..27b9635 100644 (file)
@@ -1,17 +1,8 @@
-//SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
  * Author:
  *      Sandy Huang <hjc@rock-chips.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  */
 
 #ifdef CONFIG_ROCKCHIP_RGB
index 4463d38..e2942c9 100644 (file)
@@ -440,13 +440,10 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity)
 
        while ((entity->dependency =
                        sched->ops->dependency(sched_job, entity))) {
+               trace_drm_sched_job_wait_dep(sched_job, entity->dependency);
 
-               if (drm_sched_entity_add_dependency_cb(entity)) {
-
-                       trace_drm_sched_job_wait_dep(sched_job,
-                                                    entity->dependency);
+               if (drm_sched_entity_add_dependency_cb(entity))
                        return NULL;
-               }
        }
 
        /* skip jobs from entity that marked guilty */
index 0420f5c..cf45d0f 100644 (file)
@@ -761,6 +761,7 @@ static int sun4i_tcon_init_clocks(struct device *dev,
                        return PTR_ERR(tcon->sclk0);
                }
        }
+       clk_prepare_enable(tcon->sclk0);
 
        if (tcon->quirks->has_channel_1) {
                tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");
@@ -775,6 +776,7 @@ static int sun4i_tcon_init_clocks(struct device *dev,
 
 static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon)
 {
+       clk_disable_unprepare(tcon->sclk0);
        clk_disable_unprepare(tcon->clk);
 }
 
index 9d9e814..d7b409a 100644 (file)
@@ -1,4 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
+
 #include "vkms_drv.h"
 #include <linux/crc32.h>
 #include <drm/drm_atomic.h>
index 177bbcb..eb56ee8 100644 (file)
@@ -1,10 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
 
 #include "vkms_drv.h"
 #include <drm/drm_atomic_helper.h>
index 8308787..7dcbecb 100644 (file)
@@ -1,9 +1,4 @@
-/*
- * 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
 
 /**
  * DOC: vkms (Virtual Kernel Modesetting)
index e4469cd..81f1cfb 100644 (file)
@@ -1,3 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
 #ifndef _VKMS_DRV_H_
 #define _VKMS_DRV_H_
 
index 80311da..138b0bb 100644 (file)
@@ -1,10 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
 
 #include <linux/shmem_fs.h>
 
index 271a0eb..4173e4f 100644 (file)
@@ -1,10 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
 
 #include "vkms_drv.h"
 #include <drm/drm_crtc_helper.h>
index 4188176..0e67d2d 100644 (file)
@@ -1,10 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
 
 #include "vkms_drv.h"
 #include <drm/drm_plane_helper.h>
index 25afb1d..7ef5dcb 100644 (file)
@@ -26,6 +26,7 @@
  **************************************************************************/
 #include <linux/module.h>
 #include <linux/console.h>
+#include <linux/dma-mapping.h>
 
 #include <drm/drmP.h>
 #include "vmwgfx_drv.h"
@@ -34,7 +35,6 @@
 #include <drm/ttm/ttm_placement.h>
 #include <drm/ttm/ttm_bo_driver.h>
 #include <drm/ttm/ttm_module.h>
-#include <linux/intel-iommu.h>
 
 #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices"
 #define VMWGFX_CHIP_SVGAII 0
@@ -546,6 +546,21 @@ static void vmw_get_initial_size(struct vmw_private *dev_priv)
 }
 
 /**
+ * vmw_assume_iommu - Figure out whether coherent dma-remapping might be
+ * taking place.
+ * @dev: Pointer to the struct drm_device.
+ *
+ * Return: true if iommu present, false otherwise.
+ */
+static bool vmw_assume_iommu(struct drm_device *dev)
+{
+       const struct dma_map_ops *ops = get_dma_ops(dev->dev);
+
+       return !dma_is_direct(ops) && ops &&
+               ops->map_page != dma_direct_map_page;
+}
+
+/**
  * vmw_dma_select_mode - Determine how DMA mappings should be set up for this
  * system.
  *
@@ -565,55 +580,27 @@ static int vmw_dma_select_mode(struct vmw_private *dev_priv)
                [vmw_dma_alloc_coherent] = "Using coherent TTM pages.",
                [vmw_dma_map_populate] = "Keeping DMA mappings.",
                [vmw_dma_map_bind] = "Giving up DMA mappings early."};
-#ifdef CONFIG_X86
-       const struct dma_map_ops *dma_ops = get_dma_ops(dev_priv->dev->dev);
 
-#ifdef CONFIG_INTEL_IOMMU
-       if (intel_iommu_enabled) {
+       if (vmw_force_coherent)
+               dev_priv->map_mode = vmw_dma_alloc_coherent;
+       else if (vmw_assume_iommu(dev_priv->dev))
                dev_priv->map_mode = vmw_dma_map_populate;
-               goto out_fixup;
-       }
-#endif
-
-       if (!(vmw_force_iommu || vmw_force_coherent)) {
+       else if (!vmw_force_iommu)
                dev_priv->map_mode = vmw_dma_phys;
-               DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]);
-               return 0;
-       }
-
-       dev_priv->map_mode = vmw_dma_map_populate;
-
-       if (dma_ops && dma_ops->sync_single_for_cpu)
+       else if (IS_ENABLED(CONFIG_SWIOTLB) && swiotlb_nr_tbl())
                dev_priv->map_mode = vmw_dma_alloc_coherent;
-#ifdef CONFIG_SWIOTLB
-       if (swiotlb_nr_tbl() == 0)
+       else
                dev_priv->map_mode = vmw_dma_map_populate;
-#endif
 
-#ifdef CONFIG_INTEL_IOMMU
-out_fixup:
-#endif
-       if (dev_priv->map_mode == vmw_dma_map_populate &&
-           vmw_restrict_iommu)
+       if (dev_priv->map_mode == vmw_dma_map_populate && vmw_restrict_iommu)
                dev_priv->map_mode = vmw_dma_map_bind;
 
-       if (vmw_force_coherent)
-               dev_priv->map_mode = vmw_dma_alloc_coherent;
-
-#if !defined(CONFIG_SWIOTLB) && !defined(CONFIG_INTEL_IOMMU)
-       /*
-        * No coherent page pool
-        */
-       if (dev_priv->map_mode == vmw_dma_alloc_coherent)
+       /* No TTM coherent page pool? FIXME: Ask TTM instead! */
+        if (!(IS_ENABLED(CONFIG_SWIOTLB) || IS_ENABLED(CONFIG_INTEL_IOMMU)) &&
+           (dev_priv->map_mode == vmw_dma_alloc_coherent))
                return -EINVAL;
-#endif
-
-#else /* CONFIG_X86 */
-       dev_priv->map_mode = vmw_dma_map_populate;
-#endif /* CONFIG_X86 */
 
        DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]);
-
        return 0;
 }
 
@@ -625,24 +612,20 @@ out_fixup:
  * With 32-bit we can only handle 32 bit PFNs. Optionally set that
  * restriction also for 64-bit systems.
  */
-#ifdef CONFIG_INTEL_IOMMU
 static int vmw_dma_masks(struct vmw_private *dev_priv)
 {
        struct drm_device *dev = dev_priv->dev;
+       int ret = 0;
 
-       if (intel_iommu_enabled &&
+       ret = dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(64));
+       if (dev_priv->map_mode != vmw_dma_phys &&
            (sizeof(unsigned long) == 4 || vmw_restrict_dma_mask)) {
                DRM_INFO("Restricting DMA addresses to 44 bits.\n");
-               return dma_set_mask(dev->dev, DMA_BIT_MASK(44));
+               return dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(44));
        }
-       return 0;
-}
-#else
-static int vmw_dma_masks(struct vmw_private *dev_priv)
-{
-       return 0;
+
+       return ret;
 }
-#endif
 
 static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
 {
index f2d13a7..88b8178 100644 (file)
@@ -3570,7 +3570,7 @@ int vmw_execbuf_fence_commands(struct drm_file *file_priv,
                *p_fence = NULL;
        }
 
-       return 0;
+       return ret;
 }
 
 /**
index b351fb5..ed2f678 100644 (file)
@@ -1646,7 +1646,7 @@ static int vmw_kms_check_topology(struct drm_device *dev,
                struct drm_connector_state *conn_state;
                struct vmw_connector_state *vmw_conn_state;
 
-               if (!du->pref_active) {
+               if (!du->pref_active && new_crtc_state->enable) {
                        ret = -EINVAL;
                        goto clean;
                }
@@ -2554,8 +2554,8 @@ void vmw_kms_helper_validation_finish(struct vmw_private *dev_priv,
                                      user_fence_rep)
 {
        struct vmw_fence_obj *fence = NULL;
-       uint32_t handle;
-       int ret;
+       uint32_t handle = 0;
+       int ret = 0;
 
        if (file_priv || user_fence_rep || vmw_validation_has_bos(ctx) ||
            out_fence)
index 474b00e..0a7d439 100644 (file)
@@ -898,8 +898,8 @@ static struct ipu_devtype ipu_type_imx51 = {
        .cpmem_ofs = 0x1f000000,
        .srm_ofs = 0x1f040000,
        .tpm_ofs = 0x1f060000,
-       .csi0_ofs = 0x1f030000,
-       .csi1_ofs = 0x1f038000,
+       .csi0_ofs = 0x1e030000,
+       .csi1_ofs = 0x1e038000,
        .ic_ofs = 0x1e020000,
        .disp0_ofs = 0x1e040000,
        .disp1_ofs = 0x1e048000,
@@ -914,8 +914,8 @@ static struct ipu_devtype ipu_type_imx53 = {
        .cpmem_ofs = 0x07000000,
        .srm_ofs = 0x07040000,
        .tpm_ofs = 0x07060000,
-       .csi0_ofs = 0x07030000,
-       .csi1_ofs = 0x07038000,
+       .csi0_ofs = 0x06030000,
+       .csi1_ofs = 0x06038000,
        .ic_ofs = 0x06020000,
        .disp0_ofs = 0x06040000,
        .disp1_ofs = 0x06048000,
index 2f8db9d..4a28f3f 100644 (file)
@@ -106,6 +106,7 @@ struct ipu_pre {
        void                    *buffer_virt;
        bool                    in_use;
        unsigned int            safe_window_end;
+       unsigned int            last_bufaddr;
 };
 
 static DEFINE_MUTEX(ipu_pre_list_mutex);
@@ -185,6 +186,7 @@ void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
 
        writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF);
        writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
+       pre->last_bufaddr = bufaddr;
 
        val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) |
              IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) |
@@ -242,7 +244,11 @@ void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr)
        unsigned short current_yblock;
        u32 val;
 
+       if (bufaddr == pre->last_bufaddr)
+               return;
+
        writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
+       pre->last_bufaddr = bufaddr;
 
        do {
                if (time_after(jiffies, timeout)) {
index c530476..ac9fda1 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
+#include <linux/kfifo.h>
 #include <linux/sched/signal.h>
 #include <linux/export.h>
 #include <linux/slab.h>
@@ -661,17 +662,12 @@ EXPORT_SYMBOL_GPL(hid_dump_device);
 /* enqueue string to 'events' ring buffer */
 void hid_debug_event(struct hid_device *hdev, char *buf)
 {
-       unsigned i;
        struct hid_debug_list *list;
        unsigned long flags;
 
        spin_lock_irqsave(&hdev->debug_list_lock, flags);
-       list_for_each_entry(list, &hdev->debug_list, node) {
-               for (i = 0; buf[i]; i++)
-                       list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] =
-                               buf[i];
-               list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
-        }
+       list_for_each_entry(list, &hdev->debug_list, node)
+               kfifo_in(&list->hid_debug_fifo, buf, strlen(buf));
        spin_unlock_irqrestore(&hdev->debug_list_lock, flags);
 
        wake_up_interruptible(&hdev->debug_wait);
@@ -722,8 +718,7 @@ void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 valu
        hid_debug_event(hdev, buf);
 
        kfree(buf);
-        wake_up_interruptible(&hdev->debug_wait);
-
+       wake_up_interruptible(&hdev->debug_wait);
 }
 EXPORT_SYMBOL_GPL(hid_dump_input);
 
@@ -1083,8 +1078,8 @@ static int hid_debug_events_open(struct inode *inode, struct file *file)
                goto out;
        }
 
-       if (!(list->hid_debug_buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_KERNEL))) {
-               err = -ENOMEM;
+       err = kfifo_alloc(&list->hid_debug_fifo, HID_DEBUG_FIFOSIZE, GFP_KERNEL);
+       if (err) {
                kfree(list);
                goto out;
        }
@@ -1104,77 +1099,57 @@ static ssize_t hid_debug_events_read(struct file *file, char __user *buffer,
                size_t count, loff_t *ppos)
 {
        struct hid_debug_list *list = file->private_data;
-       int ret = 0, len;
+       int ret = 0, copied;
        DECLARE_WAITQUEUE(wait, current);
 
        mutex_lock(&list->read_mutex);
-       while (ret == 0) {
-               if (list->head == list->tail) {
-                       add_wait_queue(&list->hdev->debug_wait, &wait);
-                       set_current_state(TASK_INTERRUPTIBLE);
-
-                       while (list->head == list->tail) {
-                               if (file->f_flags & O_NONBLOCK) {
-                                       ret = -EAGAIN;
-                                       break;
-                               }
-                               if (signal_pending(current)) {
-                                       ret = -ERESTARTSYS;
-                                       break;
-                               }
+       if (kfifo_is_empty(&list->hid_debug_fifo)) {
+               add_wait_queue(&list->hdev->debug_wait, &wait);
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               while (kfifo_is_empty(&list->hid_debug_fifo)) {
+                       if (file->f_flags & O_NONBLOCK) {
+                               ret = -EAGAIN;
+                               break;
+                       }
 
-                               if (!list->hdev || !list->hdev->debug) {
-                                       ret = -EIO;
-                                       set_current_state(TASK_RUNNING);
-                                       goto out;
-                               }
+                       if (signal_pending(current)) {
+                               ret = -ERESTARTSYS;
+                               break;
+                       }
 
-                               /* allow O_NONBLOCK from other threads */
-                               mutex_unlock(&list->read_mutex);
-                               schedule();
-                               mutex_lock(&list->read_mutex);
-                               set_current_state(TASK_INTERRUPTIBLE);
+                       /* if list->hdev is NULL we cannot remove_wait_queue().
+                        * if list->hdev->debug is 0 then hid_debug_unregister()
+                        * was already called and list->hdev is being destroyed.
+                        * if we add remove_wait_queue() here we can hit a race.
+                        */
+                       if (!list->hdev || !list->hdev->debug) {
+                               ret = -EIO;
+                               set_current_state(TASK_RUNNING);
+                               goto out;
                        }
 
-                       set_current_state(TASK_RUNNING);
-                       remove_wait_queue(&list->hdev->debug_wait, &wait);
+                       /* allow O_NONBLOCK from other threads */
+                       mutex_unlock(&list->read_mutex);
+                       schedule();
+                       mutex_lock(&list->read_mutex);
+                       set_current_state(TASK_INTERRUPTIBLE);
                }
 
-               if (ret)
-                       goto out;
+               __set_current_state(TASK_RUNNING);
+               remove_wait_queue(&list->hdev->debug_wait, &wait);
 
-               /* pass the ringbuffer contents to userspace */
-copy_rest:
-               if (list->tail == list->head)
+               if (ret)
                        goto out;
-               if (list->tail > list->head) {
-                       len = list->tail - list->head;
-                       if (len > count)
-                               len = count;
-
-                       if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       ret += len;
-                       list->head += len;
-               } else {
-                       len = HID_DEBUG_BUFSIZE - list->head;
-                       if (len > count)
-                               len = count;
-
-                       if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       list->head = 0;
-                       ret += len;
-                       count -= len;
-                       if (count > 0)
-                               goto copy_rest;
-               }
-
        }
+
+       /* pass the fifo content to userspace, locking is not needed with only
+        * one concurrent reader and one concurrent writer
+        */
+       ret = kfifo_to_user(&list->hid_debug_fifo, buffer, count, &copied);
+       if (ret)
+               goto out;
+       ret = copied;
 out:
        mutex_unlock(&list->read_mutex);
        return ret;
@@ -1185,7 +1160,7 @@ static __poll_t hid_debug_events_poll(struct file *file, poll_table *wait)
        struct hid_debug_list *list = file->private_data;
 
        poll_wait(file, &list->hdev->debug_wait, wait);
-       if (list->head != list->tail)
+       if (!kfifo_is_empty(&list->hid_debug_fifo))
                return EPOLLIN | EPOLLRDNORM;
        if (!list->hdev->debug)
                return EPOLLERR | EPOLLHUP;
@@ -1200,7 +1175,7 @@ static int hid_debug_events_release(struct inode *inode, struct file *file)
        spin_lock_irqsave(&list->hdev->debug_list_lock, flags);
        list_del(&list->node);
        spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags);
-       kfree(list->hid_debug_buf);
+       kfifo_free(&list->hid_debug_fifo);
        kfree(list);
 
        return 0;
@@ -1246,4 +1221,3 @@ void hid_debug_exit(void)
 {
        debugfs_remove_recursive(hid_debug_root);
 }
-
index 4adec4a..59ee01f 100644 (file)
@@ -3594,7 +3594,8 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
                        fan5pin |= cr1b & BIT(5);
                        fan5pin |= creb & BIT(5);
 
-                       fan6pin = creb & BIT(3);
+                       fan6pin = !dsw_en && (cr2d & BIT(1));
+                       fan6pin |= creb & BIT(3);
 
                        pwm5pin |= cr2d & BIT(7);
                        pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
index b1086bf..cd9c65f 100644 (file)
@@ -1500,8 +1500,7 @@ static int omap_i2c_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int omap_i2c_runtime_suspend(struct device *dev)
+static int __maybe_unused omap_i2c_runtime_suspend(struct device *dev)
 {
        struct omap_i2c_dev *omap = dev_get_drvdata(dev);
 
@@ -1527,7 +1526,7 @@ static int omap_i2c_runtime_suspend(struct device *dev)
        return 0;
 }
 
-static int omap_i2c_runtime_resume(struct device *dev)
+static int __maybe_unused omap_i2c_runtime_resume(struct device *dev)
 {
        struct omap_i2c_dev *omap = dev_get_drvdata(dev);
 
@@ -1542,20 +1541,18 @@ static int omap_i2c_runtime_resume(struct device *dev)
 }
 
 static const struct dev_pm_ops omap_i2c_pm_ops = {
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                                     pm_runtime_force_resume)
        SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend,
                           omap_i2c_runtime_resume, NULL)
 };
-#define OMAP_I2C_PM_OPS (&omap_i2c_pm_ops)
-#else
-#define OMAP_I2C_PM_OPS NULL
-#endif /* CONFIG_PM */
 
 static struct platform_driver omap_i2c_driver = {
        .probe          = omap_i2c_probe,
        .remove         = omap_i2c_remove,
        .driver         = {
                .name   = "omap_i2c",
-               .pm     = OMAP_I2C_PM_OPS,
+               .pm     = &omap_i2c_pm_ops,
                .of_match_table = of_match_ptr(omap_i2c_of_match),
        },
 };
index c39f89d..2dc628d 100644 (file)
@@ -1828,7 +1828,7 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
 
        ret = i3c_master_retrieve_dev_info(newdev);
        if (ret)
-               goto err_free_dev;
+               goto err_detach_dev;
 
        olddev = i3c_master_search_i3c_dev_duplicate(newdev);
        if (olddev) {
index f8c00b9..bb03079 100644 (file)
@@ -419,12 +419,9 @@ static void dw_i3c_master_enqueue_xfer(struct dw_i3c_master *master,
        spin_unlock_irqrestore(&master->xferqueue.lock, flags);
 }
 
-static void dw_i3c_master_dequeue_xfer(struct dw_i3c_master *master,
-                                      struct dw_i3c_xfer *xfer)
+static void dw_i3c_master_dequeue_xfer_locked(struct dw_i3c_master *master,
+                                             struct dw_i3c_xfer *xfer)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&master->xferqueue.lock, flags);
        if (master->xferqueue.cur == xfer) {
                u32 status;
 
@@ -439,6 +436,15 @@ static void dw_i3c_master_dequeue_xfer(struct dw_i3c_master *master,
        } else {
                list_del_init(&xfer->node);
        }
+}
+
+static void dw_i3c_master_dequeue_xfer(struct dw_i3c_master *master,
+                                      struct dw_i3c_xfer *xfer)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&master->xferqueue.lock, flags);
+       dw_i3c_master_dequeue_xfer_locked(master, xfer);
        spin_unlock_irqrestore(&master->xferqueue.lock, flags);
 }
 
@@ -494,7 +500,7 @@ static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr)
        complete(&xfer->comp);
 
        if (ret < 0) {
-               dw_i3c_master_dequeue_xfer(master, xfer);
+               dw_i3c_master_dequeue_xfer_locked(master, xfer);
                writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_RESUME,
                       master->regs + DEVICE_CTRL);
        }
index da58020..33a28cd 100644 (file)
@@ -235,21 +235,28 @@ EXPORT_SYMBOL_GPL(ide_prep_sense);
 
 int ide_queue_sense_rq(ide_drive_t *drive, void *special)
 {
-       struct request *sense_rq = drive->sense_rq;
+       ide_hwif_t *hwif = drive->hwif;
+       struct request *sense_rq;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hwif->lock, flags);
 
        /* deferred failure from ide_prep_sense() */
        if (!drive->sense_rq_armed) {
                printk(KERN_WARNING PFX "%s: error queuing a sense request\n",
                       drive->name);
+               spin_unlock_irqrestore(&hwif->lock, flags);
                return -ENOMEM;
        }
 
+       sense_rq = drive->sense_rq;
        ide_req(sense_rq)->special = special;
        drive->sense_rq_armed = false;
 
        drive->hwif->rq = NULL;
 
        ide_insert_request_head(drive, sense_rq);
+       spin_unlock_irqrestore(&hwif->lock, flags);
        return 0;
 }
 EXPORT_SYMBOL_GPL(ide_queue_sense_rq);
index 8445b48..b137f27 100644 (file)
@@ -68,8 +68,10 @@ int ide_end_rq(ide_drive_t *drive, struct request *rq, blk_status_t error,
        }
 
        if (!blk_update_request(rq, error, nr_bytes)) {
-               if (rq == drive->sense_rq)
+               if (rq == drive->sense_rq) {
                        drive->sense_rq = NULL;
+                       drive->sense_rq_active = false;
+               }
 
                __blk_mq_end_request(rq, error);
                return 0;
@@ -451,16 +453,11 @@ void ide_requeue_and_plug(ide_drive_t *drive, struct request *rq)
                blk_mq_delay_run_hw_queue(q->queue_hw_ctx[0], 3);
 }
 
-/*
- * Issue a new request to a device.
- */
-blk_status_t ide_queue_rq(struct blk_mq_hw_ctx *hctx,
-                         const struct blk_mq_queue_data *bd)
+blk_status_t ide_issue_rq(ide_drive_t *drive, struct request *rq,
+                         bool local_requeue)
 {
-       ide_drive_t     *drive = hctx->queue->queuedata;
-       ide_hwif_t      *hwif = drive->hwif;
+       ide_hwif_t *hwif = drive->hwif;
        struct ide_host *host = hwif->host;
-       struct request  *rq = bd->rq;
        ide_startstop_t startstop;
 
        if (!blk_rq_is_passthrough(rq) && !(rq->rq_flags & RQF_DONTPREP)) {
@@ -474,8 +471,6 @@ blk_status_t ide_queue_rq(struct blk_mq_hw_ctx *hctx,
        if (ide_lock_host(host, hwif))
                return BLK_STS_DEV_RESOURCE;
 
-       blk_mq_start_request(rq);
-
        spin_lock_irq(&hwif->lock);
 
        if (!ide_lock_port(hwif)) {
@@ -511,18 +506,6 @@ repeat:
                drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED);
 
                /*
-                * we know that the queue isn't empty, but this can happen
-                * if ->prep_rq() decides to kill a request
-                */
-               if (!rq) {
-                       rq = bd->rq;
-                       if (!rq) {
-                               ide_unlock_port(hwif);
-                               goto out;
-                       }
-               }
-
-               /*
                 * Sanity: don't accept a request that isn't a PM request
                 * if we are currently power managed. This is very important as
                 * blk_stop_queue() doesn't prevent the blk_fetch_request()
@@ -560,9 +543,12 @@ repeat:
                }
        } else {
 plug_device:
+               if (local_requeue)
+                       list_add(&rq->queuelist, &drive->rq_list);
                spin_unlock_irq(&hwif->lock);
                ide_unlock_host(host);
-               ide_requeue_and_plug(drive, rq);
+               if (!local_requeue)
+                       ide_requeue_and_plug(drive, rq);
                return BLK_STS_OK;
        }
 
@@ -573,6 +559,26 @@ out:
        return BLK_STS_OK;
 }
 
+/*
+ * Issue a new request to a device.
+ */
+blk_status_t ide_queue_rq(struct blk_mq_hw_ctx *hctx,
+                         const struct blk_mq_queue_data *bd)
+{
+       ide_drive_t *drive = hctx->queue->queuedata;
+       ide_hwif_t *hwif = drive->hwif;
+
+       spin_lock_irq(&hwif->lock);
+       if (drive->sense_rq_active) {
+               spin_unlock_irq(&hwif->lock);
+               return BLK_STS_DEV_RESOURCE;
+       }
+       spin_unlock_irq(&hwif->lock);
+
+       blk_mq_start_request(bd->rq);
+       return ide_issue_rq(drive, bd->rq, false);
+}
+
 static int drive_is_ready(ide_drive_t *drive)
 {
        ide_hwif_t *hwif = drive->hwif;
@@ -893,13 +899,8 @@ EXPORT_SYMBOL_GPL(ide_pad_transfer);
 
 void ide_insert_request_head(ide_drive_t *drive, struct request *rq)
 {
-       ide_hwif_t *hwif = drive->hwif;
-       unsigned long flags;
-
-       spin_lock_irqsave(&hwif->lock, flags);
+       drive->sense_rq_active = true;
        list_add_tail(&rq->queuelist, &drive->rq_list);
-       spin_unlock_irqrestore(&hwif->lock, flags);
-
        kblockd_schedule_work(&drive->rq_work);
 }
 EXPORT_SYMBOL_GPL(ide_insert_request_head);
index 102aa3b..8af7af6 100644 (file)
@@ -54,7 +54,9 @@ static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
        scsi_req(rq)->cmd[0] = REQ_UNPARK_HEADS;
        scsi_req(rq)->cmd_len = 1;
        ide_req(rq)->type = ATA_PRIV_MISC;
+       spin_lock_irq(&hwif->lock);
        ide_insert_request_head(drive, rq);
+       spin_unlock_irq(&hwif->lock);
 
 out:
        return;
index 63627be..5aeaca2 100644 (file)
@@ -1159,18 +1159,27 @@ static void drive_rq_insert_work(struct work_struct *work)
        ide_drive_t *drive = container_of(work, ide_drive_t, rq_work);
        ide_hwif_t *hwif = drive->hwif;
        struct request *rq;
+       blk_status_t ret;
        LIST_HEAD(list);
 
-       spin_lock_irq(&hwif->lock);
-       if (!list_empty(&drive->rq_list))
-               list_splice_init(&drive->rq_list, &list);
-       spin_unlock_irq(&hwif->lock);
+       blk_mq_quiesce_queue(drive->queue);
 
-       while (!list_empty(&list)) {
-               rq = list_first_entry(&list, struct request, queuelist);
+       ret = BLK_STS_OK;
+       spin_lock_irq(&hwif->lock);
+       while (!list_empty(&drive->rq_list)) {
+               rq = list_first_entry(&drive->rq_list, struct request, queuelist);
                list_del_init(&rq->queuelist);
-               blk_execute_rq_nowait(drive->queue, rq->rq_disk, rq, true, NULL);
+
+               spin_unlock_irq(&hwif->lock);
+               ret = ide_issue_rq(drive, rq, true);
+               spin_lock_irq(&hwif->lock);
        }
+       spin_unlock_irq(&hwif->lock);
+
+       blk_mq_unquiesce_queue(drive->queue);
+
+       if (ret != BLK_STS_OK)
+               kblockd_schedule_work(&drive->rq_work);
 }
 
 static const u8 ide_hwif_to_major[] =
index 031d568..4e339cf 100644 (file)
 #include <linux/iio/machine.h>
 #include <linux/iio/driver.h>
 
-#define AXP288_ADC_EN_MASK             0xF1
-#define AXP288_ADC_TS_PIN_GPADC                0xF2
-#define AXP288_ADC_TS_PIN_ON           0xF3
+/*
+ * This mask enables all ADCs except for the battery temp-sensor (TS), that is
+ * left as-is to avoid breaking charging on devices without a temp-sensor.
+ */
+#define AXP288_ADC_EN_MASK                             0xF0
+#define AXP288_ADC_TS_ENABLE                           0x01
+
+#define AXP288_ADC_TS_CURRENT_ON_OFF_MASK              GENMASK(1, 0)
+#define AXP288_ADC_TS_CURRENT_OFF                      (0 << 0)
+#define AXP288_ADC_TS_CURRENT_ON_WHEN_CHARGING         (1 << 0)
+#define AXP288_ADC_TS_CURRENT_ON_ONDEMAND              (2 << 0)
+#define AXP288_ADC_TS_CURRENT_ON                       (3 << 0)
 
 enum axp288_adc_id {
        AXP288_ADC_TS,
@@ -44,6 +53,7 @@ enum axp288_adc_id {
 struct axp288_adc_info {
        int irq;
        struct regmap *regmap;
+       bool ts_enabled;
 };
 
 static const struct iio_chan_spec axp288_adc_channels[] = {
@@ -115,21 +125,33 @@ static int axp288_adc_read_channel(int *val, unsigned long address,
        return IIO_VAL_INT;
 }
 
-static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
-                               unsigned long address)
+/*
+ * The current-source used for the battery temp-sensor (TS) is shared
+ * with the GPADC. For proper fuel-gauge and charger operation the TS
+ * current-source needs to be permanently on. But to read the GPADC we
+ * need to temporary switch the TS current-source to ondemand, so that
+ * the GPADC can use it, otherwise we will always read an all 0 value.
+ */
+static int axp288_adc_set_ts(struct axp288_adc_info *info,
+                            unsigned int mode, unsigned long address)
 {
        int ret;
 
-       /* channels other than GPADC do not need to switch TS pin */
+       /* No need to switch the current-source if the TS pin is disabled */
+       if (!info->ts_enabled)
+               return 0;
+
+       /* Channels other than GPADC do not need the current source */
        if (address != AXP288_GP_ADC_H)
                return 0;
 
-       ret = regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
+       ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+                                AXP288_ADC_TS_CURRENT_ON_OFF_MASK, mode);
        if (ret)
                return ret;
 
        /* When switching to the GPADC pin give things some time to settle */
-       if (mode == AXP288_ADC_TS_PIN_GPADC)
+       if (mode == AXP288_ADC_TS_CURRENT_ON_ONDEMAND)
                usleep_range(6000, 10000);
 
        return 0;
@@ -145,14 +167,14 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev,
        mutex_lock(&indio_dev->mlock);
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
-               if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
+               if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON_ONDEMAND,
                                        chan->address)) {
                        dev_err(&indio_dev->dev, "GPADC mode\n");
                        ret = -EINVAL;
                        break;
                }
                ret = axp288_adc_read_channel(val, chan->address, info->regmap);
-               if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
+               if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON,
                                                chan->address))
                        dev_err(&indio_dev->dev, "TS pin restore\n");
                break;
@@ -164,13 +186,35 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev,
        return ret;
 }
 
-static int axp288_adc_set_state(struct regmap *regmap)
+static int axp288_adc_initialize(struct axp288_adc_info *info)
 {
-       /* ADC should be always enabled for internal FG to function */
-       if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
-               return -EIO;
+       int ret, adc_enable_val;
+
+       /*
+        * Determine if the TS pin is enabled and set the TS current-source
+        * accordingly.
+        */
+       ret = regmap_read(info->regmap, AXP20X_ADC_EN1, &adc_enable_val);
+       if (ret)
+               return ret;
+
+       if (adc_enable_val & AXP288_ADC_TS_ENABLE) {
+               info->ts_enabled = true;
+               ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+                                        AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
+                                        AXP288_ADC_TS_CURRENT_ON);
+       } else {
+               info->ts_enabled = false;
+               ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+                                        AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
+                                        AXP288_ADC_TS_CURRENT_OFF);
+       }
+       if (ret)
+               return ret;
 
-       return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
+       /* Turn on the ADC for all channels except TS, leave TS as is */
+       return regmap_update_bits(info->regmap, AXP20X_ADC_EN1,
+                                 AXP288_ADC_EN_MASK, AXP288_ADC_EN_MASK);
 }
 
 static const struct iio_info axp288_adc_iio_info = {
@@ -200,7 +244,7 @@ static int axp288_adc_probe(struct platform_device *pdev)
         * Set ADC to enabled state at all time, including system suspend.
         * otherwise internal fuel gauge functionality may be affected.
         */
-       ret = axp288_adc_set_state(axp20x->regmap);
+       ret = axp288_adc_initialize(info);
        if (ret) {
                dev_err(&pdev->dev, "unable to enable ADC device\n");
                return ret;
index 184d686..8b4568e 100644 (file)
@@ -41,6 +41,7 @@
 
 #define ADS8688_VREF_MV                        4096
 #define ADS8688_REALBITS               16
+#define ADS8688_MAX_CHANNELS           8
 
 /*
  * enum ads8688_range - ADS8688 reference voltage range
@@ -385,7 +386,7 @@ static irqreturn_t ads8688_trigger_handler(int irq, void *p)
 {
        struct iio_poll_func *pf = p;
        struct iio_dev *indio_dev = pf->indio_dev;
-       u16 buffer[8];
+       u16 buffer[ADS8688_MAX_CHANNELS + sizeof(s64)/sizeof(u16)];
        int i, j = 0;
 
        for (i = 0; i < indio_dev->masklength; i++) {
index a406ad3..3a20cb5 100644 (file)
@@ -444,9 +444,8 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
        case IIO_CHAN_INFO_SCALE:
                switch (chan->type) {
                case IIO_TEMP:
-                       *val = 1; /* 0.01 */
-                       *val2 = 100;
-                       break;
+                       *val = 10;
+                       return IIO_VAL_INT;
                case IIO_PH:
                        *val = 1; /* 0.001 */
                        *val2 = 1000;
@@ -477,7 +476,7 @@ static int atlas_write_raw(struct iio_dev *indio_dev,
                           int val, int val2, long mask)
 {
        struct atlas_data *data = iio_priv(indio_dev);
-       __be32 reg = cpu_to_be32(val);
+       __be32 reg = cpu_to_be32(val / 10);
 
        if (val2 != 0 || val < 0 || val > 20000)
                return -EINVAL;
index 3cd830d..6167343 100644 (file)
@@ -267,7 +267,6 @@ static inline int ib_mad_enforce_security(struct ib_mad_agent_private *map,
 #endif
 
 struct ib_device *ib_device_get_by_index(u32 ifindex);
-void ib_device_put(struct ib_device *device);
 /* RDMA device netlink */
 void nldev_init(void);
 void nldev_exit(void);
index 8872453..238ec42 100644 (file)
@@ -156,19 +156,26 @@ struct ib_device *ib_device_get_by_index(u32 index)
        down_read(&lists_rwsem);
        device = __ib_device_get_by_index(index);
        if (device) {
-               /* Do not return a device if unregistration has started. */
-               if (!refcount_inc_not_zero(&device->refcount))
+               if (!ib_device_try_get(device))
                        device = NULL;
        }
        up_read(&lists_rwsem);
        return device;
 }
 
+/**
+ * ib_device_put - Release IB device reference
+ * @device: device whose reference to be released
+ *
+ * ib_device_put() releases reference to the IB device to allow it to be
+ * unregistered and eventually free.
+ */
 void ib_device_put(struct ib_device *device)
 {
        if (refcount_dec_and_test(&device->refcount))
                complete(&device->unreg_completion);
 }
+EXPORT_SYMBOL(ib_device_put);
 
 static struct ib_device *__ib_device_get_by_name(const char *name)
 {
@@ -303,7 +310,6 @@ struct ib_device *ib_alloc_device(size_t size)
        rwlock_init(&device->client_data_lock);
        INIT_LIST_HEAD(&device->client_data_list);
        INIT_LIST_HEAD(&device->port_list);
-       refcount_set(&device->refcount, 1);
        init_completion(&device->unreg_completion);
 
        return device;
@@ -620,6 +626,7 @@ int ib_register_device(struct ib_device *device, const char *name,
                goto cg_cleanup;
        }
 
+       refcount_set(&device->refcount, 1);
        device->reg_state = IB_DEV_REGISTERED;
 
        list_for_each_entry(client, &client_list, list)
index a4ec430..acb882f 100644 (file)
@@ -352,6 +352,8 @@ struct ib_umem_odp *ib_alloc_odp_umem(struct ib_ucontext_per_mm *per_mm,
        umem->writable   = 1;
        umem->is_odp = 1;
        odp_data->per_mm = per_mm;
+       umem->owning_mm  = per_mm->mm;
+       mmgrab(umem->owning_mm);
 
        mutex_init(&odp_data->umem_mutex);
        init_completion(&odp_data->notifier_completion);
@@ -384,6 +386,7 @@ struct ib_umem_odp *ib_alloc_odp_umem(struct ib_ucontext_per_mm *per_mm,
 out_page_list:
        vfree(odp_data->page_list);
 out_odp_data:
+       mmdrop(umem->owning_mm);
        kfree(odp_data);
        return ERR_PTR(ret);
 }
index 2890a77..5f36683 100644 (file)
@@ -204,6 +204,9 @@ void ib_uverbs_release_file(struct kref *ref)
        if (atomic_dec_and_test(&file->device->refcount))
                ib_uverbs_comp_dev(file->device);
 
+       if (file->async_file)
+               kref_put(&file->async_file->ref,
+                        ib_uverbs_release_async_event_file);
        put_device(&file->device->dev);
        kfree(file);
 }
@@ -964,11 +967,19 @@ void uverbs_user_mmap_disassociate(struct ib_uverbs_file *ufile)
 
                /* Get an arbitrary mm pointer that hasn't been cleaned yet */
                mutex_lock(&ufile->umap_lock);
-               if (!list_empty(&ufile->umaps)) {
-                       mm = list_first_entry(&ufile->umaps,
-                                             struct rdma_umap_priv, list)
-                                    ->vma->vm_mm;
-                       mmget(mm);
+               while (!list_empty(&ufile->umaps)) {
+                       int ret;
+
+                       priv = list_first_entry(&ufile->umaps,
+                                               struct rdma_umap_priv, list);
+                       mm = priv->vma->vm_mm;
+                       ret = mmget_not_zero(mm);
+                       if (!ret) {
+                               list_del_init(&priv->list);
+                               mm = NULL;
+                               continue;
+                       }
+                       break;
                }
                mutex_unlock(&ufile->umap_lock);
                if (!mm)
@@ -1096,10 +1107,6 @@ static int ib_uverbs_close(struct inode *inode, struct file *filp)
        list_del_init(&file->list);
        mutex_unlock(&file->device->lists_mutex);
 
-       if (file->async_file)
-               kref_put(&file->async_file->ref,
-                        ib_uverbs_release_async_event_file);
-
        kref_put(&file->ref, ib_uverbs_release_file);
 
        return 0;
index 5030ec4..2a3f2f0 100644 (file)
@@ -168,12 +168,18 @@ void copy_port_attr_to_resp(struct ib_port_attr *attr,
 static int UVERBS_HANDLER(UVERBS_METHOD_QUERY_PORT)(
        struct uverbs_attr_bundle *attrs)
 {
-       struct ib_device *ib_dev = attrs->ufile->device->ib_dev;
+       struct ib_device *ib_dev;
        struct ib_port_attr attr = {};
        struct ib_uverbs_query_port_resp_ex resp = {};
+       struct ib_ucontext *ucontext;
        int ret;
        u8 port_num;
 
+       ucontext = ib_uverbs_get_ucontext(attrs);
+       if (IS_ERR(ucontext))
+               return PTR_ERR(ucontext);
+       ib_dev = ucontext->device;
+
        /* FIXME: Extend the UAPI_DEF_OBJ_NEEDS_FN stuff.. */
        if (!ib_dev->ops.query_port)
                return -EOPNOTSUPP;
index c22ebc7..f9a7e9d 100644 (file)
@@ -488,7 +488,7 @@ static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma)
                vmf = 1;
                break;
        case STATUS:
-               if (flags & (unsigned long)(VM_WRITE | VM_EXEC)) {
+               if (flags & VM_WRITE) {
                        ret = -EPERM;
                        goto done;
                }
index 88242fe..bf96067 100644 (file)
@@ -987,7 +987,6 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
            opcode == IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE) {
                wc.ex.imm_data = packet->ohdr->u.ud.imm_data;
                wc.wc_flags = IB_WC_WITH_IMM;
-               tlen -= sizeof(u32);
        } else if (opcode == IB_OPCODE_UD_SEND_ONLY) {
                wc.ex.imm_data = 0;
                wc.wc_flags = 0;
index 960b194..12deacf 100644 (file)
@@ -210,6 +210,7 @@ struct ib_srq *hns_roce_create_srq(struct ib_pd *pd,
                                   struct ib_udata *udata)
 {
        struct hns_roce_dev *hr_dev = to_hr_dev(pd->device);
+       struct hns_roce_ib_create_srq_resp resp = {};
        struct hns_roce_srq *srq;
        int srq_desc_size;
        int srq_buf_size;
@@ -378,16 +379,21 @@ struct ib_srq *hns_roce_create_srq(struct ib_pd *pd,
 
        srq->event = hns_roce_ib_srq_event;
        srq->ibsrq.ext.xrc.srq_num = srq->srqn;
+       resp.srqn = srq->srqn;
 
        if (udata) {
-               if (ib_copy_to_udata(udata, &srq->srqn, sizeof(__u32))) {
+               if (ib_copy_to_udata(udata, &resp,
+                                    min(udata->outlen, sizeof(resp)))) {
                        ret = -EFAULT;
-                       goto err_wrid;
+                       goto err_srqc_alloc;
                }
        }
 
        return &srq->ibsrq;
 
+err_srqc_alloc:
+       hns_roce_srq_free(hr_dev, srq);
+
 err_wrid:
        kvfree(srq->wrid);
 
index 25439da..936ee13 100644 (file)
@@ -1411,7 +1411,7 @@ int mlx4_ib_send_to_wire(struct mlx4_ib_dev *dev, int slave, u8 port,
 
        sqp_mad = (struct mlx4_mad_snd_buf *) (sqp->tx_ring[wire_tx_ix].buf.addr);
        if (sqp->tx_ring[wire_tx_ix].ah)
-               rdma_destroy_ah(sqp->tx_ring[wire_tx_ix].ah, 0);
+               mlx4_ib_destroy_ah(sqp->tx_ring[wire_tx_ix].ah, 0);
        sqp->tx_ring[wire_tx_ix].ah = ah;
        ib_dma_sync_single_for_cpu(&dev->ib_dev,
                                   sqp->tx_ring[wire_tx_ix].buf.map,
@@ -1902,7 +1902,7 @@ static void mlx4_ib_sqp_comp_worker(struct work_struct *work)
                if (wc.status == IB_WC_SUCCESS) {
                        switch (wc.opcode) {
                        case IB_WC_SEND:
-                               rdma_destroy_ah(sqp->tx_ring[wc.wr_id &
+                               mlx4_ib_destroy_ah(sqp->tx_ring[wc.wr_id &
                                              (MLX4_NUM_TUNNEL_BUFS - 1)].ah, 0);
                                sqp->tx_ring[wc.wr_id & (MLX4_NUM_TUNNEL_BUFS - 1)].ah
                                        = NULL;
@@ -1931,7 +1931,7 @@ static void mlx4_ib_sqp_comp_worker(struct work_struct *work)
                                 " status = %d, wrid = 0x%llx\n",
                                 ctx->slave, wc.status, wc.wr_id);
                        if (!MLX4_TUN_IS_RECV(wc.wr_id)) {
-                               rdma_destroy_ah(sqp->tx_ring[wc.wr_id &
+                               mlx4_ib_destroy_ah(sqp->tx_ring[wc.wr_id &
                                              (MLX4_NUM_TUNNEL_BUFS - 1)].ah, 0);
                                sqp->tx_ring[wc.wr_id & (MLX4_NUM_TUNNEL_BUFS - 1)].ah
                                        = NULL;
index 356bccc..6bcc63a 100644 (file)
@@ -345,3 +345,40 @@ int mlx5_cmd_alloc_q_counter(struct mlx5_core_dev *dev, u16 *counter_id,
                                       counter_set_id);
        return err;
 }
+
+int mlx5_cmd_mad_ifc(struct mlx5_core_dev *dev, const void *inb, void *outb,
+                    u16 opmod, u8 port)
+{
+       int outlen = MLX5_ST_SZ_BYTES(mad_ifc_out);
+       int inlen = MLX5_ST_SZ_BYTES(mad_ifc_in);
+       int err = -ENOMEM;
+       void *data;
+       void *resp;
+       u32 *out;
+       u32 *in;
+
+       in = kzalloc(inlen, GFP_KERNEL);
+       out = kzalloc(outlen, GFP_KERNEL);
+       if (!in || !out)
+               goto out;
+
+       MLX5_SET(mad_ifc_in, in, opcode, MLX5_CMD_OP_MAD_IFC);
+       MLX5_SET(mad_ifc_in, in, op_mod, opmod);
+       MLX5_SET(mad_ifc_in, in, port, port);
+
+       data = MLX5_ADDR_OF(mad_ifc_in, in, mad);
+       memcpy(data, inb, MLX5_FLD_SZ_BYTES(mad_ifc_in, mad));
+
+       err = mlx5_cmd_exec(dev, in, inlen, out, outlen);
+       if (err)
+               goto out;
+
+       resp = MLX5_ADDR_OF(mad_ifc_out, out, response_mad_packet);
+       memcpy(outb, resp,
+              MLX5_FLD_SZ_BYTES(mad_ifc_out, response_mad_packet));
+
+out:
+       kfree(out);
+       kfree(in);
+       return err;
+}
index 1e76dc6..923a7b9 100644 (file)
@@ -63,4 +63,6 @@ int mlx5_cmd_xrcd_alloc(struct mlx5_core_dev *dev, u32 *xrcdn, u16 uid);
 int mlx5_cmd_xrcd_dealloc(struct mlx5_core_dev *dev, u32 xrcdn, u16 uid);
 int mlx5_cmd_alloc_q_counter(struct mlx5_core_dev *dev, u16 *counter_id,
                             u16 uid);
+int mlx5_cmd_mad_ifc(struct mlx5_core_dev *dev, const void *inb, void *outb,
+                    u16 opmod, u8 port);
 #endif /* MLX5_IB_CMD_H */
index e8a1e44..798591a 100644 (file)
@@ -630,8 +630,7 @@ const struct uapi_definition mlx5_ib_flow_defs[] = {
                UAPI_DEF_IS_OBJ_SUPPORTED(flow_is_supported)),
        UAPI_DEF_CHAIN_OBJ_TREE(
                UVERBS_OBJECT_FLOW,
-               &mlx5_ib_fs,
-               UAPI_DEF_IS_OBJ_SUPPORTED(flow_is_supported)),
+               &mlx5_ib_fs),
        UAPI_DEF_CHAIN_OBJ_TREE(UVERBS_OBJECT_FLOW_ACTION,
                                &mlx5_ib_flow_actions),
        {},
index 46a9ddc..4700cff 100644 (file)
@@ -3,10 +3,11 @@
  * Copyright (c) 2018 Mellanox Technologies. All rights reserved.
  */
 
+#include <linux/mlx5/vport.h>
 #include "ib_rep.h"
 #include "srq.h"
 
-static const struct mlx5_ib_profile rep_profile = {
+static const struct mlx5_ib_profile vf_rep_profile = {
        STAGE_CREATE(MLX5_IB_STAGE_INIT,
                     mlx5_ib_stage_init_init,
                     mlx5_ib_stage_init_cleanup),
@@ -46,30 +47,16 @@ static const struct mlx5_ib_profile rep_profile = {
 };
 
 static int
-mlx5_ib_nic_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
-{
-       struct mlx5_ib_dev *ibdev;
-
-       ibdev = mlx5_ib_rep_to_dev(rep);
-       if (!__mlx5_ib_add(ibdev, ibdev->profile))
-               return -EINVAL;
-       return 0;
-}
-
-static void
-mlx5_ib_nic_rep_unload(struct mlx5_eswitch_rep *rep)
-{
-       struct mlx5_ib_dev *ibdev;
-
-       ibdev = mlx5_ib_rep_to_dev(rep);
-       __mlx5_ib_remove(ibdev, ibdev->profile, MLX5_IB_STAGE_MAX);
-}
-
-static int
 mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
 {
+       const struct mlx5_ib_profile *profile;
        struct mlx5_ib_dev *ibdev;
 
+       if (rep->vport == MLX5_VPORT_UPLINK)
+               profile = &uplink_rep_profile;
+       else
+               profile = &vf_rep_profile;
+
        ibdev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*ibdev));
        if (!ibdev)
                return -ENOMEM;
@@ -78,7 +65,7 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
        ibdev->mdev = dev;
        ibdev->num_ports = max(MLX5_CAP_GEN(dev, num_ports),
                               MLX5_CAP_GEN(dev, num_vhca_ports));
-       if (!__mlx5_ib_add(ibdev, &rep_profile))
+       if (!__mlx5_ib_add(ibdev, profile))
                return -EINVAL;
 
        rep->rep_if[REP_IB].priv = ibdev;
@@ -105,53 +92,23 @@ static void *mlx5_ib_vport_get_proto_dev(struct mlx5_eswitch_rep *rep)
        return mlx5_ib_rep_to_dev(rep);
 }
 
-static void mlx5_ib_rep_register_vf_vports(struct mlx5_ib_dev *dev)
-{
-       struct mlx5_eswitch *esw   = dev->mdev->priv.eswitch;
-       int total_vfs = MLX5_TOTAL_VPORTS(dev->mdev);
-       int vport;
-
-       for (vport = 1; vport < total_vfs; vport++) {
-               struct mlx5_eswitch_rep_if rep_if = {};
-
-               rep_if.load = mlx5_ib_vport_rep_load;
-               rep_if.unload = mlx5_ib_vport_rep_unload;
-               rep_if.get_proto_dev = mlx5_ib_vport_get_proto_dev;
-               mlx5_eswitch_register_vport_rep(esw, vport, &rep_if, REP_IB);
-       }
-}
-
-static void mlx5_ib_rep_unregister_vf_vports(struct mlx5_ib_dev *dev)
+void mlx5_ib_register_vport_reps(struct mlx5_core_dev *mdev)
 {
-       struct mlx5_eswitch *esw   = dev->mdev->priv.eswitch;
-       int total_vfs = MLX5_TOTAL_VPORTS(dev->mdev);
-       int vport;
-
-       for (vport = 1; vport < total_vfs; vport++)
-               mlx5_eswitch_unregister_vport_rep(esw, vport, REP_IB);
-}
-
-void mlx5_ib_register_vport_reps(struct mlx5_ib_dev *dev)
-{
-       struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
+       struct mlx5_eswitch *esw = mdev->priv.eswitch;
        struct mlx5_eswitch_rep_if rep_if = {};
 
-       rep_if.load = mlx5_ib_nic_rep_load;
-       rep_if.unload = mlx5_ib_nic_rep_unload;
+       rep_if.load = mlx5_ib_vport_rep_load;
+       rep_if.unload = mlx5_ib_vport_rep_unload;
        rep_if.get_proto_dev = mlx5_ib_vport_get_proto_dev;
-       rep_if.priv = dev;
-
-       mlx5_eswitch_register_vport_rep(esw, 0, &rep_if, REP_IB);
 
-       mlx5_ib_rep_register_vf_vports(dev);
+       mlx5_eswitch_register_vport_reps(esw, &rep_if, REP_IB);
 }
 
-void mlx5_ib_unregister_vport_reps(struct mlx5_ib_dev *dev)
+void mlx5_ib_unregister_vport_reps(struct mlx5_core_dev *mdev)
 {
-       struct mlx5_eswitch *esw   = dev->mdev->priv.eswitch;
+       struct mlx5_eswitch *esw mdev->priv.eswitch;
 
-       mlx5_ib_rep_unregister_vf_vports(dev); /* VFs vports */
-       mlx5_eswitch_unregister_vport_rep(esw, 0, REP_IB); /* UPLINK PF*/
+       mlx5_eswitch_unregister_vport_reps(esw, REP_IB);
 }
 
 u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw)
index 2ba7363..798d41e 100644 (file)
 #include "mlx5_ib.h"
 
 #ifdef CONFIG_MLX5_ESWITCH
+extern const struct mlx5_ib_profile uplink_rep_profile;
+
 u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw);
 struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
                                          int vport_index);
 struct mlx5_ib_dev *mlx5_ib_get_uplink_ibdev(struct mlx5_eswitch *esw);
 struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
                                           int vport_index);
-void mlx5_ib_register_vport_reps(struct mlx5_ib_dev *dev);
-void mlx5_ib_unregister_vport_reps(struct mlx5_ib_dev *dev);
+void mlx5_ib_register_vport_reps(struct mlx5_core_dev *mdev);
+void mlx5_ib_unregister_vport_reps(struct mlx5_core_dev *mdev);
 int create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
                              struct mlx5_ib_sq *sq);
 struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
@@ -48,8 +50,8 @@ struct mlx5_eswitch_rep *mlx5_ib_vport_rep(struct mlx5_eswitch *esw,
        return NULL;
 }
 
-static inline void mlx5_ib_register_vport_reps(struct mlx5_ib_dev *dev) {}
-static inline void mlx5_ib_unregister_vport_reps(struct mlx5_ib_dev *dev) {}
+static inline void mlx5_ib_register_vport_reps(struct mlx5_core_dev *mdev) {}
+static inline void mlx5_ib_unregister_vport_reps(struct mlx5_core_dev *mdev) {}
 static inline int create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
                                            struct mlx5_ib_sq *sq)
 {
index 5586384..6c529e6 100644 (file)
@@ -36,6 +36,7 @@
 #include <rdma/ib_smi.h>
 #include <rdma/ib_pma.h>
 #include "mlx5_ib.h"
+#include "cmd.h"
 
 enum {
        MLX5_IB_VENDOR_CLASS1 = 0x9,
@@ -51,9 +52,10 @@ static bool can_do_mad_ifc(struct mlx5_ib_dev *dev, u8 port_num,
        return dev->mdev->port_caps[port_num - 1].has_smi;
 }
 
-int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey,
-                u8 port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
-                const void *in_mad, void *response_mad)
+static int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey,
+                       int ignore_bkey, u8 port, const struct ib_wc *in_wc,
+                       const struct ib_grh *in_grh, const void *in_mad,
+                       void *response_mad)
 {
        u8 op_modifier = 0;
 
@@ -68,7 +70,8 @@ int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey,
        if (ignore_bkey || !in_wc)
                op_modifier |= 0x2;
 
-       return mlx5_core_mad_ifc(dev->mdev, in_mad, response_mad, op_modifier, port);
+       return mlx5_cmd_mad_ifc(dev->mdev, in_mad, response_mad, op_modifier,
+                               port);
 }
 
 static int process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
index 94fe253..581ae11 100644 (file)
@@ -331,8 +331,8 @@ out:
        spin_unlock(&port->mp.mpi_lock);
 }
 
-static int translate_eth_proto_oper(u32 eth_proto_oper, u8 *active_speed,
-                                   u8 *active_width)
+static int translate_eth_legacy_proto_oper(u32 eth_proto_oper, u8 *active_speed,
+                                          u8 *active_width)
 {
        switch (eth_proto_oper) {
        case MLX5E_PROT_MASK(MLX5E_1000BASE_CX_SGMII):
@@ -389,10 +389,66 @@ static int translate_eth_proto_oper(u32 eth_proto_oper, u8 *active_speed,
        return 0;
 }
 
+static int translate_eth_ext_proto_oper(u32 eth_proto_oper, u8 *active_speed,
+                                       u8 *active_width)
+{
+       switch (eth_proto_oper) {
+       case MLX5E_PROT_MASK(MLX5E_SGMII_100M):
+       case MLX5E_PROT_MASK(MLX5E_1000BASE_X_SGMII):
+               *active_width = IB_WIDTH_1X;
+               *active_speed = IB_SPEED_SDR;
+               break;
+       case MLX5E_PROT_MASK(MLX5E_5GBASE_R):
+               *active_width = IB_WIDTH_1X;
+               *active_speed = IB_SPEED_DDR;
+               break;
+       case MLX5E_PROT_MASK(MLX5E_10GBASE_XFI_XAUI_1):
+               *active_width = IB_WIDTH_1X;
+               *active_speed = IB_SPEED_QDR;
+               break;
+       case MLX5E_PROT_MASK(MLX5E_40GBASE_XLAUI_4_XLPPI_4):
+               *active_width = IB_WIDTH_4X;
+               *active_speed = IB_SPEED_QDR;
+               break;
+       case MLX5E_PROT_MASK(MLX5E_25GAUI_1_25GBASE_CR_KR):
+               *active_width = IB_WIDTH_1X;
+               *active_speed = IB_SPEED_EDR;
+               break;
+       case MLX5E_PROT_MASK(MLX5E_50GAUI_2_LAUI_2_50GBASE_CR2_KR2):
+       case MLX5E_PROT_MASK(MLX5E_50GAUI_1_LAUI_1_50GBASE_CR_KR):
+               *active_width = IB_WIDTH_1X;
+               *active_speed = IB_SPEED_HDR;
+               break;
+       case MLX5E_PROT_MASK(MLX5E_100GAUI_2_100GBASE_CR2_KR2):
+               *active_width = IB_WIDTH_2X;
+               *active_speed = IB_SPEED_HDR;
+               break;
+       case MLX5E_PROT_MASK(MLX5E_200GAUI_4_200GBASE_CR4_KR4):
+               *active_width = IB_WIDTH_4X;
+               *active_speed = IB_SPEED_HDR;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int translate_eth_proto_oper(u32 eth_proto_oper, u8 *active_speed,
+                                   u8 *active_width, bool ext)
+{
+       return ext ?
+               translate_eth_ext_proto_oper(eth_proto_oper, active_speed,
+                                            active_width) :
+               translate_eth_legacy_proto_oper(eth_proto_oper, active_speed,
+                                               active_width);
+}
+
 static int mlx5_query_port_roce(struct ib_device *device, u8 port_num,
                                struct ib_port_attr *props)
 {
        struct mlx5_ib_dev *dev = to_mdev(device);
+       u32 out[MLX5_ST_SZ_DW(ptys_reg)] = {0};
        struct mlx5_core_dev *mdev;
        struct net_device *ndev, *upper;
        enum ib_mtu ndev_ib_mtu;
@@ -400,6 +456,7 @@ static int mlx5_query_port_roce(struct ib_device *device, u8 port_num,
        u16 qkey_viol_cntr;
        u32 eth_prot_oper;
        u8 mdev_port_num;
+       bool ext;
        int err;
 
        mdev = mlx5_ib_get_native_port_mdev(dev, port_num, &mdev_port_num);
@@ -416,16 +473,18 @@ static int mlx5_query_port_roce(struct ib_device *device, u8 port_num,
        /* Possible bad flows are checked before filling out props so in case
         * of an error it will still be zeroed out.
         */
-       err = mlx5_query_port_eth_proto_oper(mdev, &eth_prot_oper,
-                                            mdev_port_num);
+       err = mlx5_query_port_ptys(mdev, out, sizeof(out), MLX5_PTYS_EN,
+                                  mdev_port_num);
        if (err)
                goto out;
+       ext = MLX5_CAP_PCAM_FEATURE(dev->mdev, ptys_extended_ethernet);
+       eth_prot_oper = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_oper);
 
        props->active_width     = IB_WIDTH_4X;
        props->active_speed     = IB_SPEED_QDR;
 
        translate_eth_proto_oper(eth_prot_oper, &props->active_speed,
-                                &props->active_width);
+                                &props->active_width, ext);
 
        props->port_cap_flags |= IB_PORT_CM_SUP;
        props->ip_gids = true;
@@ -6386,7 +6445,7 @@ static const struct mlx5_ib_profile pf_profile = {
                     mlx5_ib_stage_delay_drop_cleanup),
 };
 
-static const struct mlx5_ib_profile nic_rep_profile = {
+const struct mlx5_ib_profile uplink_rep_profile = {
        STAGE_CREATE(MLX5_IB_STAGE_INIT,
                     mlx5_ib_stage_init_init,
                     mlx5_ib_stage_init_cleanup),
@@ -6479,6 +6538,12 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
 
        printk_once(KERN_INFO "%s", mlx5_version);
 
+       if (MLX5_ESWITCH_MANAGER(mdev) &&
+           mlx5_ib_eswitch_mode(mdev->priv.eswitch) == SRIOV_OFFLOADS) {
+               mlx5_ib_register_vport_reps(mdev);
+               return mdev;
+       }
+
        port_type_cap = MLX5_CAP_GEN(mdev, port_type);
        ll = mlx5_port_type_cap_to_rdma_ll(port_type_cap);
 
@@ -6493,14 +6558,6 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
        dev->num_ports = max(MLX5_CAP_GEN(mdev, num_ports),
                             MLX5_CAP_GEN(mdev, num_vhca_ports));
 
-       if (MLX5_ESWITCH_MANAGER(mdev) &&
-           mlx5_ib_eswitch_mode(mdev->priv.eswitch) == SRIOV_OFFLOADS) {
-               dev->rep = mlx5_ib_vport_rep(mdev->priv.eswitch, 0);
-               dev->profile = &nic_rep_profile;
-               mlx5_ib_register_vport_reps(dev);
-               return dev;
-       }
-
        return __mlx5_ib_add(dev, &pf_profile);
 }
 
@@ -6509,6 +6566,11 @@ static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context)
        struct mlx5_ib_multiport_info *mpi;
        struct mlx5_ib_dev *dev;
 
+       if (MLX5_ESWITCH_MANAGER(mdev) && context == mdev) {
+               mlx5_ib_unregister_vport_reps(mdev);
+               return;
+       }
+
        if (mlx5_core_is_mp_slave(mdev)) {
                mpi = context;
                mutex_lock(&mlx5_ib_multiport_mutex);
@@ -6520,10 +6582,7 @@ static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context)
        }
 
        dev = context;
-       if (dev->profile == &nic_rep_profile)
-               mlx5_ib_unregister_vport_reps(dev);
-       else
-               __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
+       __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
 
        ib_dealloc_device((struct ib_device *)dev);
 }
index b06d3b1..eedba0d 100644 (file)
@@ -587,6 +587,7 @@ struct mlx5_ib_mr {
        struct mlx5_ib_mr      *parent;
        atomic_t                num_leaf_free;
        wait_queue_head_t       q_leaf_free;
+       struct mlx5_async_work  cb_work;
 };
 
 struct mlx5_ib_mw {
@@ -944,6 +945,7 @@ struct mlx5_ib_dev {
        struct mlx5_memic       memic;
        u16                     devx_whitelist_uid;
        struct mlx5_srq_table   srq_table;
+       struct mlx5_async_ctx   async_ctx;
 };
 
 static inline struct mlx5_ib_cq *to_mibcq(struct mlx5_core_cq *mcq)
@@ -1038,9 +1040,6 @@ void mlx5_ib_db_unmap_user(struct mlx5_ib_ucontext *context, struct mlx5_db *db)
 void __mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq);
 void mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq);
 void mlx5_ib_free_srq_wqe(struct mlx5_ib_srq *srq, int wqe_index);
-int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey,
-                u8 port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
-                const void *in_mad, void *response_mad);
 struct ib_ah *mlx5_ib_create_ah(struct ib_pd *pd, struct rdma_ah_attr *ah_attr,
                                u32 flags, struct ib_udata *udata);
 int mlx5_ib_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr);
index fd6ea1f..bf2b6ea 100644 (file)
@@ -123,9 +123,10 @@ static void update_odp_mr(struct mlx5_ib_mr *mr)
 }
 #endif
 
-static void reg_mr_callback(int status, void *context)
+static void reg_mr_callback(int status, struct mlx5_async_work *context)
 {
-       struct mlx5_ib_mr *mr = context;
+       struct mlx5_ib_mr *mr =
+               container_of(context, struct mlx5_ib_mr, cb_work);
        struct mlx5_ib_dev *dev = mr->dev;
        struct mlx5_mr_cache *cache = &dev->cache;
        int c = order2idx(dev, mr->order);
@@ -216,9 +217,9 @@ static int add_keys(struct mlx5_ib_dev *dev, int c, int num)
                ent->pending++;
                spin_unlock_irq(&ent->lock);
                err = mlx5_core_create_mkey_cb(dev->mdev, &mr->mmkey,
-                                              in, inlen,
+                                              &dev->async_ctx, in, inlen,
                                               mr->out, sizeof(mr->out),
-                                              reg_mr_callback, mr);
+                                              reg_mr_callback, &mr->cb_work);
                if (err) {
                        spin_lock_irq(&ent->lock);
                        ent->pending--;
@@ -679,6 +680,7 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
                return -ENOMEM;
        }
 
+       mlx5_cmd_init_async_ctx(dev->mdev, &dev->async_ctx);
        timer_setup(&dev->delay_timer, delay_time_func, 0);
        for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
                ent = &cache->ent[i];
@@ -725,33 +727,6 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
        return 0;
 }
 
-static void wait_for_async_commands(struct mlx5_ib_dev *dev)
-{
-       struct mlx5_mr_cache *cache = &dev->cache;
-       struct mlx5_cache_ent *ent;
-       int total = 0;
-       int i;
-       int j;
-
-       for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
-               ent = &cache->ent[i];
-               for (j = 0 ; j < 1000; j++) {
-                       if (!ent->pending)
-                               break;
-                       msleep(50);
-               }
-       }
-       for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
-               ent = &cache->ent[i];
-               total += ent->pending;
-       }
-
-       if (total)
-               mlx5_ib_warn(dev, "aborted while there are %d pending mr requests\n", total);
-       else
-               mlx5_ib_warn(dev, "done with all pending requests\n");
-}
-
 int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev)
 {
        int i;
@@ -763,12 +738,12 @@ int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev)
        flush_workqueue(dev->cache.wq);
 
        mlx5_mr_cache_debugfs_cleanup(dev);
+       mlx5_cmd_cleanup_async_ctx(&dev->async_ctx);
 
        for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++)
                clean_keys(dev, i);
 
        destroy_workqueue(dev->cache.wq);
-       wait_for_async_commands(dev);
        del_timer_sync(&dev->delay_timer);
 
        return 0;
index 01e0f62..4ee3296 100644 (file)
@@ -1595,10 +1595,12 @@ static void mlx5_ib_prefetch_mr_work(struct work_struct *work)
        struct prefetch_mr_work *w =
                container_of(work, struct prefetch_mr_work, work);
 
-       if (w->dev->ib_dev.reg_state == IB_DEV_REGISTERED)
+       if (ib_device_try_get(&w->dev->ib_dev)) {
                mlx5_ib_prefetch_sg_list(w->dev, w->pf_flags, w->sg_list,
                                         w->num_sge);
-
+               ib_device_put(&w->dev->ib_dev);
+       }
+       put_device(&w->dev->ib_dev.dev);
        kfree(w);
 }
 
@@ -1617,15 +1619,13 @@ int mlx5_ib_advise_mr_prefetch(struct ib_pd *pd,
                return mlx5_ib_prefetch_sg_list(dev, pf_flags, sg_list,
                                                num_sge);
 
-       if (dev->ib_dev.reg_state != IB_DEV_REGISTERED)
-               return -ENODEV;
-
        work = kvzalloc(struct_size(work, sg_list, num_sge), GFP_KERNEL);
        if (!work)
                return -ENOMEM;
 
        memcpy(work->sg_list, sg_list, num_sge * sizeof(struct ib_sge));
 
+       get_device(&dev->ib_dev.dev);
        work->dev = dev;
        work->pf_flags = pf_flags;
        work->num_sge = num_sge;
index dd2ae64..7db778d 100644 (file)
@@ -1912,14 +1912,16 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd,
                }
 
                if (!check_flags_mask(ucmd.flags,
+                                     MLX5_QP_FLAG_ALLOW_SCATTER_CQE |
+                                     MLX5_QP_FLAG_BFREG_INDEX |
+                                     MLX5_QP_FLAG_PACKET_BASED_CREDIT_MODE |
+                                     MLX5_QP_FLAG_SCATTER_CQE |
                                      MLX5_QP_FLAG_SIGNATURE |
-                                             MLX5_QP_FLAG_SCATTER_CQE |
-                                             MLX5_QP_FLAG_TUNNEL_OFFLOADS |
-                                             MLX5_QP_FLAG_BFREG_INDEX |
-                                             MLX5_QP_FLAG_TYPE_DCT |
-                                             MLX5_QP_FLAG_TYPE_DCI |
-                                             MLX5_QP_FLAG_ALLOW_SCATTER_CQE |
-                                             MLX5_QP_FLAG_PACKET_BASED_CREDIT_MODE))
+                                     MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_MC |
+                                     MLX5_QP_FLAG_TIR_ALLOW_SELF_LB_UC |
+                                     MLX5_QP_FLAG_TUNNEL_OFFLOADS |
+                                     MLX5_QP_FLAG_TYPE_DCI |
+                                     MLX5_QP_FLAG_TYPE_DCT))
                        return -EINVAL;
 
                err = get_qp_user_index(to_mucontext(pd->uobject->context),
index 868da0e..445ea19 100644 (file)
@@ -512,7 +512,6 @@ void qib_ud_rcv(struct qib_ibport *ibp, struct ib_header *hdr,
            opcode == IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE) {
                wc.ex.imm_data = ohdr->u.ud.imm_data;
                wc.wc_flags = IB_WC_WITH_IMM;
-               tlen -= sizeof(u32);
        } else if (opcode == IB_OPCODE_UD_SEND_ONLY) {
                wc.ex.imm_data = 0;
                wc.wc_flags = 0;
index a1bd8cf..c6cc3e4 100644 (file)
@@ -2910,6 +2910,8 @@ send:
                        goto op_err;
                if (!ret)
                        goto rnr_nak;
+               if (wqe->length > qp->r_len)
+                       goto inv_err;
                break;
 
        case IB_WR_RDMA_WRITE_WITH_IMM:
@@ -3078,7 +3080,10 @@ op_err:
        goto err;
 
 inv_err:
-       send_status = IB_WC_REM_INV_REQ_ERR;
+       send_status =
+               sqp->ibqp.qp_type == IB_QPT_RC ?
+                       IB_WC_REM_INV_REQ_ERR :
+                       IB_WC_SUCCESS;
        wc.status = IB_WC_LOC_QP_OP_ERR;
        goto err;
 
index 1da119d..73e808c 100644 (file)
@@ -248,7 +248,6 @@ struct ipoib_cm_tx {
        struct list_head     list;
        struct net_device   *dev;
        struct ipoib_neigh  *neigh;
-       struct ipoib_path   *path;
        struct ipoib_tx_buf *tx_ring;
        unsigned int         tx_head;
        unsigned int         tx_tail;
index 0428e01..aa9dcfc 100644 (file)
@@ -1312,7 +1312,6 @@ struct ipoib_cm_tx *ipoib_cm_create_tx(struct net_device *dev, struct ipoib_path
 
        neigh->cm = tx;
        tx->neigh = neigh;
-       tx->path = path;
        tx->dev = dev;
        list_add(&tx->list, &priv->cm.start_list);
        set_bit(IPOIB_FLAG_INITIALIZED, &tx->flags);
@@ -1371,7 +1370,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
                                neigh->daddr + QPN_AND_OPTIONS_OFFSET);
                        goto free_neigh;
                }
-               memcpy(&pathrec, &p->path->pathrec, sizeof(pathrec));
+               memcpy(&pathrec, &path->pathrec, sizeof(pathrec));
 
                spin_unlock_irqrestore(&priv->lock, flags);
                netif_tx_unlock_bh(dev);
index bae0822..a7cfab3 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/of.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
-#include <linux/clk.h>
 
 /*
  * The OLPC XO-1.75 and XO-4 laptops do not have a hardware PS/2 controller.
@@ -75,7 +74,6 @@ struct olpc_apsp {
        struct serio *kbio;
        struct serio *padio;
        void __iomem *base;
-       struct clk *clk;
        int open_count;
        int irq;
 };
@@ -148,17 +146,11 @@ static int olpc_apsp_open(struct serio *port)
        struct olpc_apsp *priv = port->port_data;
        unsigned int tmp;
        unsigned long l;
-       int error;
 
        if (priv->open_count++ == 0) {
-               error = clk_prepare_enable(priv->clk);
-               if (error)
-                       return error;
-
                l = readl(priv->base + COMMAND_FIFO_STATUS);
                if (!(l & CMD_STS_MASK)) {
                        dev_err(priv->dev, "SP cannot accept commands.\n");
-                       clk_disable_unprepare(priv->clk);
                        return -EIO;
                }
 
@@ -179,8 +171,6 @@ static void olpc_apsp_close(struct serio *port)
                /* Disable interrupt 0 */
                tmp = readl(priv->base + PJ_INTERRUPT_MASK);
                writel(tmp | INT_0, priv->base + PJ_INTERRUPT_MASK);
-
-               clk_disable_unprepare(priv->clk);
        }
 }
 
@@ -208,10 +198,6 @@ static int olpc_apsp_probe(struct platform_device *pdev)
        if (priv->irq < 0)
                return priv->irq;
 
-       priv->clk = devm_clk_get(&pdev->dev, "sp");
-       if (IS_ERR(priv->clk))
-               return PTR_ERR(priv->clk);
-
        /* KEYBOARD */
        kb_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
        if (!kb_serio)
index 87ba23a..2a7b78b 100644 (file)
@@ -1991,16 +1991,13 @@ static void do_attach(struct iommu_dev_data *dev_data,
 
 static void do_detach(struct iommu_dev_data *dev_data)
 {
+       struct protection_domain *domain = dev_data->domain;
        struct amd_iommu *iommu;
        u16 alias;
 
        iommu = amd_iommu_rlookup_table[dev_data->devid];
        alias = dev_data->alias;
 
-       /* decrease reference counters */
-       dev_data->domain->dev_iommu[iommu->index] -= 1;
-       dev_data->domain->dev_cnt                 -= 1;
-
        /* Update data structures */
        dev_data->domain = NULL;
        list_del(&dev_data->list);
@@ -2010,6 +2007,16 @@ static void do_detach(struct iommu_dev_data *dev_data)
 
        /* Flush the DTE entry */
        device_flush_dte(dev_data);
+
+       /* Flush IOTLB */
+       domain_flush_tlb_pde(domain);
+
+       /* Wait for the flushes to finish */
+       domain_flush_complete(domain);
+
+       /* decrease reference counters - needs to happen after the flushes */
+       domain->dev_iommu[iommu->index] -= 1;
+       domain->dev_cnt                 -= 1;
 }
 
 /*
@@ -2617,13 +2624,13 @@ out_unmap:
                        bus_addr  = address + s->dma_address + (j << PAGE_SHIFT);
                        iommu_unmap_page(domain, bus_addr, PAGE_SIZE);
 
-                       if (--mapped_pages)
+                       if (--mapped_pages == 0)
                                goto out_free_iova;
                }
        }
 
 out_free_iova:
-       free_iova_fast(&dma_dom->iovad, address, npages);
+       free_iova_fast(&dma_dom->iovad, address >> PAGE_SHIFT, npages);
 
 out_err:
        return 0;
index 2bd9ac2..78188bf 100644 (file)
@@ -363,7 +363,7 @@ static int dmar_map_gfx = 1;
 static int dmar_forcedac;
 static int intel_iommu_strict;
 static int intel_iommu_superpage = 1;
-static int intel_iommu_sm = 1;
+static int intel_iommu_sm;
 static int iommu_identity_mapping;
 
 #define IDENTMAP_ALL           1
@@ -456,9 +456,9 @@ static int __init intel_iommu_setup(char *str)
                } else if (!strncmp(str, "sp_off", 6)) {
                        pr_info("Disable supported super page\n");
                        intel_iommu_superpage = 0;
-               } else if (!strncmp(str, "sm_off", 6)) {
-                       pr_info("Intel-IOMMU: disable scalable mode support\n");
-                       intel_iommu_sm = 0;
+               } else if (!strncmp(str, "sm_on", 5)) {
+                       pr_info("Intel-IOMMU: scalable mode supported\n");
+                       intel_iommu_sm = 1;
                } else if (!strncmp(str, "tboot_noforce", 13)) {
                        printk(KERN_INFO
                                "Intel-IOMMU: not forcing on after tboot. This could expose security risk for tboot\n");
@@ -5294,7 +5294,7 @@ static void intel_iommu_put_resv_regions(struct device *dev,
        struct iommu_resv_region *entry, *next;
 
        list_for_each_entry_safe(entry, next, head, list) {
-               if (entry->type == IOMMU_RESV_RESERVED)
+               if (entry->type == IOMMU_RESV_MSI)
                        kfree(entry);
        }
 }
index 730f7da..7e0df67 100644 (file)
@@ -441,6 +441,10 @@ static int mtk_iommu_add_device(struct device *dev)
                iommu_spec.args_count = count;
 
                mtk_iommu_create_mapping(dev, &iommu_spec);
+
+               /* dev->iommu_fwspec might have changed */
+               fwspec = dev_iommu_fwspec_get(dev);
+
                of_node_put(iommu_spec.np);
        }
 
index db20e99..c3aba3f 100644 (file)
@@ -97,9 +97,14 @@ struct its_device;
  * The ITS structure - contains most of the infrastructure, with the
  * top-level MSI domain, the command queue, the collections, and the
  * list of devices writing to it.
+ *
+ * dev_alloc_lock has to be taken for device allocations, while the
+ * spinlock must be taken to parse data structures such as the device
+ * list.
  */
 struct its_node {
        raw_spinlock_t          lock;
+       struct mutex            dev_alloc_lock;
        struct list_head        entry;
        void __iomem            *base;
        phys_addr_t             phys_base;
@@ -156,6 +161,7 @@ struct its_device {
        void                    *itt;
        u32                     nr_ites;
        u32                     device_id;
+       bool                    shared;
 };
 
 static struct {
@@ -1580,6 +1586,9 @@ static unsigned long *its_lpi_alloc(int nr_irqs, u32 *base, int *nr_ids)
                nr_irqs /= 2;
        } while (nr_irqs > 0);
 
+       if (!nr_irqs)
+               err = -ENOSPC;
+
        if (err)
                goto out;
 
@@ -2059,6 +2068,29 @@ static int __init allocate_lpi_tables(void)
        return 0;
 }
 
+static u64 its_clear_vpend_valid(void __iomem *vlpi_base)
+{
+       u32 count = 1000000;    /* 1s! */
+       bool clean;
+       u64 val;
+
+       val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
+       val &= ~GICR_VPENDBASER_Valid;
+       gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
+
+       do {
+               val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
+               clean = !(val & GICR_VPENDBASER_Dirty);
+               if (!clean) {
+                       count--;
+                       cpu_relax();
+                       udelay(1);
+               }
+       } while (!clean && count);
+
+       return val;
+}
+
 static void its_cpu_init_lpis(void)
 {
        void __iomem *rbase = gic_data_rdist_rd_base();
@@ -2144,6 +2176,30 @@ static void its_cpu_init_lpis(void)
        val |= GICR_CTLR_ENABLE_LPIS;
        writel_relaxed(val, rbase + GICR_CTLR);
 
+       if (gic_rdists->has_vlpis) {
+               void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
+
+               /*
+                * It's possible for CPU to receive VLPIs before it is
+                * sheduled as a vPE, especially for the first CPU, and the
+                * VLPI with INTID larger than 2^(IDbits+1) will be considered
+                * as out of range and dropped by GIC.
+                * So we initialize IDbits to known value to avoid VLPI drop.
+                */
+               val = (LPI_NRBITS - 1) & GICR_VPROPBASER_IDBITS_MASK;
+               pr_debug("GICv4: CPU%d: Init IDbits to 0x%llx for GICR_VPROPBASER\n",
+                       smp_processor_id(), val);
+               gits_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);
+
+               /*
+                * Also clear Valid bit of GICR_VPENDBASER, in case some
+                * ancient programming gets left in and has possibility of
+                * corrupting memory.
+                */
+               val = its_clear_vpend_valid(vlpi_base);
+               WARN_ON(val & GICR_VPENDBASER_Dirty);
+       }
+
        /* Make sure the GIC has seen the above */
        dsb(sy);
 out:
@@ -2399,13 +2455,14 @@ static void its_free_device(struct its_device *its_dev)
        kfree(its_dev);
 }
 
-static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq)
+static int its_alloc_device_irq(struct its_device *dev, int nvecs, irq_hw_number_t *hwirq)
 {
        int idx;
 
-       idx = find_first_zero_bit(dev->event_map.lpi_map,
-                                 dev->event_map.nr_lpis);
-       if (idx == dev->event_map.nr_lpis)
+       idx = bitmap_find_free_region(dev->event_map.lpi_map,
+                                     dev->event_map.nr_lpis,
+                                     get_count_order(nvecs));
+       if (idx < 0)
                return -ENOSPC;
 
        *hwirq = dev->event_map.lpi_base + idx;
@@ -2421,6 +2478,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
        struct its_device *its_dev;
        struct msi_domain_info *msi_info;
        u32 dev_id;
+       int err = 0;
 
        /*
         * We ignore "dev" entierely, and rely on the dev_id that has
@@ -2443,6 +2501,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
                return -EINVAL;
        }
 
+       mutex_lock(&its->dev_alloc_lock);
        its_dev = its_find_device(its, dev_id);
        if (its_dev) {
                /*
@@ -2450,18 +2509,22 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
                 * another alias (PCI bridge of some sort). No need to
                 * create the device.
                 */
+               its_dev->shared = true;
                pr_debug("Reusing ITT for devID %x\n", dev_id);
                goto out;
        }
 
        its_dev = its_create_device(its, dev_id, nvec, true);
-       if (!its_dev)
-               return -ENOMEM;
+       if (!its_dev) {
+               err = -ENOMEM;
+               goto out;
+       }
 
        pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec));
 out:
+       mutex_unlock(&its->dev_alloc_lock);
        info->scratchpad[0].ptr = its_dev;
-       return 0;
+       return err;
 }
 
 static struct msi_domain_ops its_msi_domain_ops = {
@@ -2501,21 +2564,21 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
        int err;
        int i;
 
-       for (i = 0; i < nr_irqs; i++) {
-               err = its_alloc_device_irq(its_dev, &hwirq);
-               if (err)
-                       return err;
+       err = its_alloc_device_irq(its_dev, nr_irqs, &hwirq);
+       if (err)
+               return err;
 
-               err = its_irq_gic_domain_alloc(domain, virq + i, hwirq);
+       for (i = 0; i < nr_irqs; i++) {
+               err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i);
                if (err)
                        return err;
 
                irq_domain_set_hwirq_and_chip(domain, virq + i,
-                                             hwirq, &its_irq_chip, its_dev);
+                                             hwirq + i, &its_irq_chip, its_dev);
                irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq + i)));
                pr_debug("ID:%d pID:%d vID:%d\n",
-                        (int)(hwirq - its_dev->event_map.lpi_base),
-                        (int) hwirq, virq + i);
+                        (int)(hwirq + i - its_dev->event_map.lpi_base),
+                        (int)(hwirq + i), virq + i);
        }
 
        return 0;
@@ -2565,6 +2628,7 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
 {
        struct irq_data *d = irq_domain_get_irq_data(domain, virq);
        struct its_device *its_dev = irq_data_get_irq_chip_data(d);
+       struct its_node *its = its_dev->its;
        int i;
 
        for (i = 0; i < nr_irqs; i++) {
@@ -2579,8 +2643,14 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
                irq_domain_reset_irq_data(data);
        }
 
-       /* If all interrupts have been freed, start mopping the floor */
-       if (bitmap_empty(its_dev->event_map.lpi_map,
+       mutex_lock(&its->dev_alloc_lock);
+
+       /*
+        * If all interrupts have been freed, start mopping the
+        * floor. This is conditionned on the device not being shared.
+        */
+       if (!its_dev->shared &&
+           bitmap_empty(its_dev->event_map.lpi_map,
                         its_dev->event_map.nr_lpis)) {
                its_lpi_free(its_dev->event_map.lpi_map,
                             its_dev->event_map.lpi_base,
@@ -2592,6 +2662,8 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
                its_free_device(its_dev);
        }
 
+       mutex_unlock(&its->dev_alloc_lock);
+
        irq_domain_free_irqs_parent(domain, virq, nr_irqs);
 }
 
@@ -2754,26 +2826,11 @@ static void its_vpe_schedule(struct its_vpe *vpe)
 static void its_vpe_deschedule(struct its_vpe *vpe)
 {
        void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
-       u32 count = 1000000;    /* 1s! */
-       bool clean;
        u64 val;
 
-       /* We're being scheduled out */
-       val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
-       val &= ~GICR_VPENDBASER_Valid;
-       gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
-
-       do {
-               val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
-               clean = !(val & GICR_VPENDBASER_Dirty);
-               if (!clean) {
-                       count--;
-                       cpu_relax();
-                       udelay(1);
-               }
-       } while (!clean && count);
+       val = its_clear_vpend_valid(vlpi_base);
 
-       if (unlikely(!clean && !count)) {
+       if (unlikely(val & GICR_VPENDBASER_Dirty)) {
                pr_err_ratelimited("ITS virtual pending table not cleaning\n");
                vpe->idai = false;
                vpe->pending_last = true;
@@ -3516,6 +3573,7 @@ static int __init its_probe_one(struct resource *res,
        }
 
        raw_spin_lock_init(&its->lock);
+       mutex_init(&its->dev_alloc_lock);
        INIT_LIST_HEAD(&its->entry);
        INIT_LIST_HEAD(&its->its_device_list);
        typer = gic_read_typer(its_base + GITS_TYPER);
index ad70e7c..fbfa7ff 100644 (file)
@@ -24,7 +24,7 @@ struct mbi_range {
        unsigned long           *bm;
 };
 
-static struct mutex            mbi_lock;
+static DEFINE_MUTEX(mbi_lock);
 static phys_addr_t             mbi_phys_base;
 static struct mbi_range                *mbi_ranges;
 static unsigned int            mbi_range_nr;
index e9256de..8b81271 100644 (file)
@@ -7,7 +7,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
@@ -16,7 +15,6 @@
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/of_gpio.h>
 #include <linux/of_irq.h>
 #include <linux/irqchip/irq-madera.h>
 #include <linux/mfd/madera/core.h>
index 25f32e1..3496b61 100644 (file)
@@ -34,6 +34,9 @@
 #define SEL_INT_PENDING                (1 << 6)
 #define SEL_INT_NUM_MASK       0x3f
 
+#define MMP2_ICU_INT_ROUTE_PJ4_IRQ     (1 << 5)
+#define MMP2_ICU_INT_ROUTE_PJ4_FIQ     (1 << 6)
+
 struct icu_chip_data {
        int                     nr_irqs;
        unsigned int            virq_base;
@@ -190,7 +193,8 @@ static const struct mmp_intc_conf mmp_conf = {
 static const struct mmp_intc_conf mmp2_conf = {
        .conf_enable    = 0x20,
        .conf_disable   = 0x0,
-       .conf_mask      = 0x7f,
+       .conf_mask      = MMP2_ICU_INT_ROUTE_PJ4_IRQ |
+                         MMP2_ICU_INT_ROUTE_PJ4_FIQ,
 };
 
 static void __exception_irq_entry mmp_handle_irq(struct pt_regs *regs)
index 6edfd4b..a93296b 100644 (file)
@@ -822,6 +822,7 @@ out_unmap:
 static const struct irq_domain_ops stm32_exti_h_domain_ops = {
        .alloc  = stm32_exti_h_domain_alloc,
        .free   = irq_domain_free_irqs_common,
+       .xlate = irq_domain_xlate_twocell,
 };
 
 static int
index 5385f57..2793333 100644 (file)
@@ -71,14 +71,17 @@ static void xtensa_mx_irq_mask(struct irq_data *d)
        unsigned int mask = 1u << d->hwirq;
 
        if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE |
-                               XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) {
-               set_er(1u << (xtensa_get_ext_irq_no(d->hwirq) -
-                                       HW_IRQ_MX_BASE), MIENG);
-       } else {
-               mask = __this_cpu_read(cached_irq_mask) & ~mask;
-               __this_cpu_write(cached_irq_mask, mask);
-               xtensa_set_sr(mask, intenable);
+                   XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) {
+               unsigned int ext_irq = xtensa_get_ext_irq_no(d->hwirq);
+
+               if (ext_irq >= HW_IRQ_MX_BASE) {
+                       set_er(1u << (ext_irq - HW_IRQ_MX_BASE), MIENG);
+                       return;
+               }
        }
+       mask = __this_cpu_read(cached_irq_mask) & ~mask;
+       __this_cpu_write(cached_irq_mask, mask);
+       xtensa_set_sr(mask, intenable);
 }
 
 static void xtensa_mx_irq_unmask(struct irq_data *d)
@@ -86,14 +89,17 @@ static void xtensa_mx_irq_unmask(struct irq_data *d)
        unsigned int mask = 1u << d->hwirq;
 
        if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE |
-                               XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) {
-               set_er(1u << (xtensa_get_ext_irq_no(d->hwirq) -
-                                       HW_IRQ_MX_BASE), MIENGSET);
-       } else {
-               mask |= __this_cpu_read(cached_irq_mask);
-               __this_cpu_write(cached_irq_mask, mask);
-               xtensa_set_sr(mask, intenable);
+                   XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) {
+               unsigned int ext_irq = xtensa_get_ext_irq_no(d->hwirq);
+
+               if (ext_irq >= HW_IRQ_MX_BASE) {
+                       set_er(1u << (ext_irq - HW_IRQ_MX_BASE), MIENGSET);
+                       return;
+               }
        }
+       mask |= __this_cpu_read(cached_irq_mask);
+       __this_cpu_write(cached_irq_mask, mask);
+       xtensa_set_sr(mask, intenable);
 }
 
 static void xtensa_mx_irq_enable(struct irq_data *d)
@@ -113,7 +119,11 @@ static void xtensa_mx_irq_ack(struct irq_data *d)
 
 static int xtensa_mx_irq_retrigger(struct irq_data *d)
 {
-       xtensa_set_sr(1 << d->hwirq, intset);
+       unsigned int mask = 1u << d->hwirq;
+
+       if (WARN_ON(mask & ~XCHAL_INTTYPE_MASK_SOFTWARE))
+               return 0;
+       xtensa_set_sr(mask, intset);
        return 1;
 }
 
index c200234..ab12328 100644 (file)
@@ -70,7 +70,11 @@ static void xtensa_irq_ack(struct irq_data *d)
 
 static int xtensa_irq_retrigger(struct irq_data *d)
 {
-       xtensa_set_sr(1 << d->hwirq, intset);
+       unsigned int mask = 1u << d->hwirq;
+
+       if (WARN_ON(mask & ~XCHAL_INTTYPE_MASK_SOFTWARE))
+               return 0;
+       xtensa_set_sr(mask, intset);
        return 1;
 }
 
index ab0b63a..e1de8b1 100644 (file)
@@ -633,7 +633,7 @@ gigaset_tty_ioctl(struct tty_struct *tty, struct file *file,
                        flush_send_queue(cs);
                        break;
                }
-               /* Pass through */
+               /* fall through */
 
        default:
                /* pass through to underlying serial device */
index 81dd465..71a8312 100644 (file)
@@ -656,7 +656,7 @@ hfcpci_fill_fifo(struct BCState *bcs)
                                schedule_event(bcs, B_ACKPENDING);
                        }
 
-                       dev_kfree_skb_any(bcs->tx_skb);
+                       dev_consume_skb_any(bcs->tx_skb);
                        bcs->tx_skb = skb_dequeue(&bcs->squeue);        /* fetch next data */
                }
                test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
index dc1cded..43700fc 100644 (file)
@@ -3642,7 +3642,7 @@ isdn_tty_edit_at(const char *p, int count, modem_info *info)
                                                break;
                                        } else
                                                m->mdmcmdl = 0;
-                                       /* Fall through, check for 'A' */
+                                       /* Fall through - check for 'A' */
                                case 0:
                                        if (c == 'A') {
                                                m->mdmcmd[m->mdmcmdl] = c;
index 2a5f666..d11fe76 100644 (file)
@@ -354,7 +354,7 @@ EncodeMatrix(unsigned char *buf, int len, unsigned char *m, int mlen)
                                printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
                                return line;
                        }
-                       /* else: fall through */
+                       /* fall through */
                case 128:
                        m[line] = 128;  /* leftmost -> set byte to 1000000 */
                        mbit = 64;      /* current bit in the matrix line */
index 15d3ca3..4ab8b1b 100644 (file)
@@ -103,7 +103,7 @@ mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
 static inline void
 mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
 {
-       struct timeval  tv;
+       struct __kernel_old_timeval     tv;
 
        if (_pms(sk)->cmask & MISDN_TIME_STAMP) {
                skb_get_timestamp(skb, &tv);
index 211ed6c..5789787 100644 (file)
@@ -170,8 +170,8 @@ dev_expire_timer(struct timer_list *t)
        spin_lock_irqsave(&timer->dev->lock, flags);
        if (timer->id >= 0)
                list_move_tail(&timer->list, &timer->dev->expired);
-       spin_unlock_irqrestore(&timer->dev->lock, flags);
        wake_up_interruptible(&timer->dev->wait);
+       spin_unlock_irqrestore(&timer->dev->lock, flags);
 }
 
 static int
index 47d4e0d..dd538e6 100644 (file)
@@ -932,7 +932,7 @@ static int dm_crypt_integrity_io_alloc(struct dm_crypt_io *io, struct bio *bio)
        if (IS_ERR(bip))
                return PTR_ERR(bip);
 
-       tag_len = io->cc->on_disk_tag_size * bio_sectors(bio);
+       tag_len = io->cc->on_disk_tag_size * (bio_sectors(bio) >> io->cc->sector_shift);
 
        bip->bip_iter.bi_size = tag_len;
        bip->bip_iter.bi_sector = io->cc->start + io->sector;
index 4eb5f8c..a20531e 100644 (file)
@@ -131,7 +131,7 @@ static void rq_end_stats(struct mapped_device *md, struct request *orig)
 static void rq_completed(struct mapped_device *md)
 {
        /* nudge anyone waiting on suspend queue */
-       if (unlikely(waitqueue_active(&md->wait)))
+       if (unlikely(wq_has_sleeper(&md->wait)))
                wake_up(&md->wait);
 
        /*
index ca8af21..e83b636 100644 (file)
@@ -257,6 +257,7 @@ struct pool {
 
        spinlock_t lock;
        struct bio_list deferred_flush_bios;
+       struct bio_list deferred_flush_completions;
        struct list_head prepared_mappings;
        struct list_head prepared_discards;
        struct list_head prepared_discards_pt2;
@@ -956,6 +957,39 @@ static void process_prepared_mapping_fail(struct dm_thin_new_mapping *m)
        mempool_free(m, &m->tc->pool->mapping_pool);
 }
 
+static void complete_overwrite_bio(struct thin_c *tc, struct bio *bio)
+{
+       struct pool *pool = tc->pool;
+       unsigned long flags;
+
+       /*
+        * If the bio has the REQ_FUA flag set we must commit the metadata
+        * before signaling its completion.
+        */
+       if (!bio_triggers_commit(tc, bio)) {
+               bio_endio(bio);
+               return;
+       }
+
+       /*
+        * Complete bio with an error if earlier I/O caused changes to the
+        * metadata that can't be committed, e.g, due to I/O errors on the
+        * metadata device.
+        */
+       if (dm_thin_aborted_changes(tc->td)) {
+               bio_io_error(bio);
+               return;
+       }
+
+       /*
+        * Batch together any bios that trigger commits and then issue a
+        * single commit for them in process_deferred_bios().
+        */
+       spin_lock_irqsave(&pool->lock, flags);
+       bio_list_add(&pool->deferred_flush_completions, bio);
+       spin_unlock_irqrestore(&pool->lock, flags);
+}
+
 static void process_prepared_mapping(struct dm_thin_new_mapping *m)
 {
        struct thin_c *tc = m->tc;
@@ -988,7 +1022,7 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m)
         */
        if (bio) {
                inc_remap_and_issue_cell(tc, m->cell, m->data_block);
-               bio_endio(bio);
+               complete_overwrite_bio(tc, bio);
        } else {
                inc_all_io_entry(tc->pool, m->cell->holder);
                remap_and_issue(tc, m->cell->holder, m->data_block);
@@ -2317,7 +2351,7 @@ static void process_deferred_bios(struct pool *pool)
 {
        unsigned long flags;
        struct bio *bio;
-       struct bio_list bios;
+       struct bio_list bios, bio_completions;
        struct thin_c *tc;
 
        tc = get_first_thin(pool);
@@ -2328,26 +2362,36 @@ static void process_deferred_bios(struct pool *pool)
        }
 
        /*
-        * If there are any deferred flush bios, we must commit
-        * the metadata before issuing them.
+        * If there are any deferred flush bios, we must commit the metadata
+        * before issuing them or signaling their completion.
         */
        bio_list_init(&bios);
+       bio_list_init(&bio_completions);
+
        spin_lock_irqsave(&pool->lock, flags);
        bio_list_merge(&bios, &pool->deferred_flush_bios);
        bio_list_init(&pool->deferred_flush_bios);
+
+       bio_list_merge(&bio_completions, &pool->deferred_flush_completions);
+       bio_list_init(&pool->deferred_flush_completions);
        spin_unlock_irqrestore(&pool->lock, flags);
 
-       if (bio_list_empty(&bios) &&
+       if (bio_list_empty(&bios) && bio_list_empty(&bio_completions) &&
            !(dm_pool_changed_this_transaction(pool->pmd) && need_commit_due_to_time(pool)))
                return;
 
        if (commit(pool)) {
+               bio_list_merge(&bios, &bio_completions);
+
                while ((bio = bio_list_pop(&bios)))
                        bio_io_error(bio);
                return;
        }
        pool->last_commit_jiffies = jiffies;
 
+       while ((bio = bio_list_pop(&bio_completions)))
+               bio_endio(bio);
+
        while ((bio = bio_list_pop(&bios)))
                generic_make_request(bio);
 }
@@ -2954,6 +2998,7 @@ static struct pool *pool_create(struct mapped_device *pool_md,
        INIT_DELAYED_WORK(&pool->no_space_timeout, do_no_space_timeout);
        spin_lock_init(&pool->lock);
        bio_list_init(&pool->deferred_flush_bios);
+       bio_list_init(&pool->deferred_flush_completions);
        INIT_LIST_HEAD(&pool->prepared_mappings);
        INIT_LIST_HEAD(&pool->prepared_discards);
        INIT_LIST_HEAD(&pool->prepared_discards_pt2);
index 2b53c38..515e6af 100644 (file)
@@ -699,7 +699,7 @@ static void end_io_acct(struct dm_io *io)
                                    true, duration, &io->stats_aux);
 
        /* nudge anyone waiting on suspend queue */
-       if (unlikely(waitqueue_active(&md->wait)))
+       if (unlikely(wq_has_sleeper(&md->wait)))
                wake_up(&md->wait);
 }
 
@@ -1336,7 +1336,11 @@ static int clone_bio(struct dm_target_io *tio, struct bio *bio,
                        return r;
        }
 
-       bio_trim(clone, sector - clone->bi_iter.bi_sector, len);
+       bio_advance(clone, to_bytes(sector - clone->bi_iter.bi_sector));
+       clone->bi_iter.bi_size = to_bytes(len);
+
+       if (bio_integrity(bio))
+               bio_integrity_trim(clone);
 
        return 0;
 }
index 1d54109..fa47249 100644 (file)
@@ -1863,6 +1863,20 @@ static void end_sync_read(struct bio *bio)
                reschedule_retry(r1_bio);
 }
 
+static void abort_sync_write(struct mddev *mddev, struct r1bio *r1_bio)
+{
+       sector_t sync_blocks = 0;
+       sector_t s = r1_bio->sector;
+       long sectors_to_go = r1_bio->sectors;
+
+       /* make sure these bits don't get cleared. */
+       do {
+               md_bitmap_end_sync(mddev->bitmap, s, &sync_blocks, 1);
+               s += sync_blocks;
+               sectors_to_go -= sync_blocks;
+       } while (sectors_to_go > 0);
+}
+
 static void end_sync_write(struct bio *bio)
 {
        int uptodate = !bio->bi_status;
@@ -1874,15 +1888,7 @@ static void end_sync_write(struct bio *bio)
        struct md_rdev *rdev = conf->mirrors[find_bio_disk(r1_bio, bio)].rdev;
 
        if (!uptodate) {
-               sector_t sync_blocks = 0;
-               sector_t s = r1_bio->sector;
-               long sectors_to_go = r1_bio->sectors;
-               /* make sure these bits doesn't get cleared. */
-               do {
-                       md_bitmap_end_sync(mddev->bitmap, s, &sync_blocks, 1);
-                       s += sync_blocks;
-                       sectors_to_go -= sync_blocks;
-               } while (sectors_to_go > 0);
+               abort_sync_write(mddev, r1_bio);
                set_bit(WriteErrorSeen, &rdev->flags);
                if (!test_and_set_bit(WantReplacement, &rdev->flags))
                        set_bit(MD_RECOVERY_NEEDED, &
@@ -2172,8 +2178,10 @@ static void sync_request_write(struct mddev *mddev, struct r1bio *r1_bio)
                     (i == r1_bio->read_disk ||
                      !test_bit(MD_RECOVERY_SYNC, &mddev->recovery))))
                        continue;
-               if (test_bit(Faulty, &conf->mirrors[i].rdev->flags))
+               if (test_bit(Faulty, &conf->mirrors[i].rdev->flags)) {
+                       abort_sync_write(mddev, r1_bio);
                        continue;
+               }
 
                bio_set_op_attrs(wbio, REQ_OP_WRITE, 0);
                if (test_bit(FailFast, &conf->mirrors[i].rdev->flags))
index ec3a5ef..cbbe6b6 100644 (file)
@@ -1935,12 +1935,14 @@ out:
 }
 
 static struct stripe_head *
-r5c_recovery_alloc_stripe(struct r5conf *conf,
-                         sector_t stripe_sect)
+r5c_recovery_alloc_stripe(
+               struct r5conf *conf,
+               sector_t stripe_sect,
+               int noblock)
 {
        struct stripe_head *sh;
 
-       sh = raid5_get_active_stripe(conf, stripe_sect, 0, 1, 0);
+       sh = raid5_get_active_stripe(conf, stripe_sect, 0, noblock, 0);
        if (!sh)
                return NULL;  /* no more stripe available */
 
@@ -2150,7 +2152,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
                                                stripe_sect);
 
                if (!sh) {
-                       sh = r5c_recovery_alloc_stripe(conf, stripe_sect);
+                       sh = r5c_recovery_alloc_stripe(conf, stripe_sect, 1);
                        /*
                         * cannot get stripe from raid5_get_active_stripe
                         * try replay some stripes
@@ -2159,20 +2161,29 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
                                r5c_recovery_replay_stripes(
                                        cached_stripe_list, ctx);
                                sh = r5c_recovery_alloc_stripe(
-                                       conf, stripe_sect);
+                                       conf, stripe_sect, 1);
                        }
                        if (!sh) {
+                               int new_size = conf->min_nr_stripes * 2;
                                pr_debug("md/raid:%s: Increasing stripe cache size to %d to recovery data on journal.\n",
                                        mdname(mddev),
-                                       conf->min_nr_stripes * 2);
-                               raid5_set_cache_size(mddev,
-                                                    conf->min_nr_stripes * 2);
-                               sh = r5c_recovery_alloc_stripe(conf,
-                                                              stripe_sect);
+                                       new_size);
+                               ret = raid5_set_cache_size(mddev, new_size);
+                               if (conf->min_nr_stripes <= new_size / 2) {
+                                       pr_err("md/raid:%s: Cannot increase cache size, ret=%d, new_size=%d, min_nr_stripes=%d, max_nr_stripes=%d\n",
+                                               mdname(mddev),
+                                               ret,
+                                               new_size,
+                                               conf->min_nr_stripes,
+                                               conf->max_nr_stripes);
+                                       return -ENOMEM;
+                               }
+                               sh = r5c_recovery_alloc_stripe(
+                                       conf, stripe_sect, 0);
                        }
                        if (!sh) {
                                pr_err("md/raid:%s: Cannot get enough stripes due to memory pressure. Recovery failed.\n",
-                                      mdname(mddev));
+                                       mdname(mddev));
                                return -ENOMEM;
                        }
                        list_add_tail(&sh->lru, cached_stripe_list);
index 4990f03..cecea90 100644 (file)
@@ -6369,6 +6369,7 @@ raid5_show_stripe_cache_size(struct mddev *mddev, char *page)
 int
 raid5_set_cache_size(struct mddev *mddev, int size)
 {
+       int result = 0;
        struct r5conf *conf = mddev->private;
 
        if (size <= 16 || size > 32768)
@@ -6385,11 +6386,14 @@ raid5_set_cache_size(struct mddev *mddev, int size)
 
        mutex_lock(&conf->cache_size_mutex);
        while (size > conf->max_nr_stripes)
-               if (!grow_one_stripe(conf, GFP_KERNEL))
+               if (!grow_one_stripe(conf, GFP_KERNEL)) {
+                       conf->min_nr_stripes = conf->max_nr_stripes;
+                       result = -ENOMEM;
                        break;
+               }
        mutex_unlock(&conf->cache_size_mutex);
 
-       return 0;
+       return result;
 }
 EXPORT_SYMBOL(raid5_set_cache_size);
 
index f461460..76f9909 100644 (file)
@@ -1419,7 +1419,7 @@ config MFD_TPS65217
 
 config MFD_TPS68470
        bool "TI TPS68470 Power Management / LED chips"
-       depends on ACPI && I2C=y
+       depends on ACPI && PCI && I2C=y
        select MFD_CORE
        select REGMAP_I2C
        select I2C_DESIGNWARE_PLATFORM
index 1fc8ea0..ca4c9cc 100644 (file)
@@ -401,8 +401,11 @@ static void mei_io_list_flush_cl(struct list_head *head,
        struct mei_cl_cb *cb, *next;
 
        list_for_each_entry_safe(cb, next, head, list) {
-               if (cl == cb->cl)
+               if (cl == cb->cl) {
                        list_del_init(&cb->list);
+                       if (cb->fop_type == MEI_FOP_READ)
+                               mei_io_cb_free(cb);
+               }
        }
 }
 
index 23739a6..bb1ee98 100644 (file)
 #define MEI_DEV_ID_CNP_H      0xA360  /* Cannon Point H */
 #define MEI_DEV_ID_CNP_H_4    0xA364  /* Cannon Point H 4 (iTouch) */
 
+#define MEI_DEV_ID_ICP_LP     0x34E0  /* Ice Lake Point LP */
+
 /*
  * MEI HW Section
  */
index e89497f..3ab946a 100644 (file)
@@ -105,6 +105,8 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
        {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H, MEI_ME_PCH12_CFG)},
        {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H_4, MEI_ME_PCH8_CFG)},
 
+       {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)},
+
        /* required last entry */
        {0, }
 };
index 2bfa3a9..744757f 100644 (file)
@@ -47,7 +47,8 @@
  * @dc: Virtio device control
  * @vpdev: VOP device which is the parent for this virtio device
  * @vr: Buffer for accessing the VRING
- * @used: Buffer for used
+ * @used_virt: Virtual address of used ring
+ * @used: DMA address of used ring
  * @used_size: Size of the used buffer
  * @reset_done: Track whether VOP reset is complete
  * @virtio_cookie: Cookie returned upon requesting a interrupt
@@ -61,6 +62,7 @@ struct _vop_vdev {
        struct mic_device_ctrl __iomem *dc;
        struct vop_device *vpdev;
        void __iomem *vr[VOP_MAX_VRINGS];
+       void *used_virt[VOP_MAX_VRINGS];
        dma_addr_t used[VOP_MAX_VRINGS];
        int used_size[VOP_MAX_VRINGS];
        struct completion reset_done;
@@ -260,12 +262,12 @@ static bool vop_notify(struct virtqueue *vq)
 static void vop_del_vq(struct virtqueue *vq, int n)
 {
        struct _vop_vdev *vdev = to_vopvdev(vq->vdev);
-       struct vring *vr = (struct vring *)(vq + 1);
        struct vop_device *vpdev = vdev->vpdev;
 
        dma_unmap_single(&vpdev->dev, vdev->used[n],
                         vdev->used_size[n], DMA_BIDIRECTIONAL);
-       free_pages((unsigned long)vr->used, get_order(vdev->used_size[n]));
+       free_pages((unsigned long)vdev->used_virt[n],
+                  get_order(vdev->used_size[n]));
        vring_del_virtqueue(vq);
        vpdev->hw_ops->iounmap(vpdev, vdev->vr[n]);
        vdev->vr[n] = NULL;
@@ -283,6 +285,26 @@ static void vop_del_vqs(struct virtio_device *dev)
                vop_del_vq(vq, idx++);
 }
 
+static struct virtqueue *vop_new_virtqueue(unsigned int index,
+                                     unsigned int num,
+                                     struct virtio_device *vdev,
+                                     bool context,
+                                     void *pages,
+                                     bool (*notify)(struct virtqueue *vq),
+                                     void (*callback)(struct virtqueue *vq),
+                                     const char *name,
+                                     void *used)
+{
+       bool weak_barriers = false;
+       struct vring vring;
+
+       vring_init(&vring, num, pages, MIC_VIRTIO_RING_ALIGN);
+       vring.used = used;
+
+       return __vring_new_virtqueue(index, vring, vdev, weak_barriers, context,
+                                    notify, callback, name);
+}
+
 /*
  * This routine will assign vring's allocated in host/io memory. Code in
  * virtio_ring.c however continues to access this io memory as if it were local
@@ -302,7 +324,6 @@ static struct virtqueue *vop_find_vq(struct virtio_device *dev,
        struct _mic_vring_info __iomem *info;
        void *used;
        int vr_size, _vr_size, err, magic;
-       struct vring *vr;
        u8 type = ioread8(&vdev->desc->type);
 
        if (index >= ioread8(&vdev->desc->num_vq))
@@ -322,17 +343,7 @@ static struct virtqueue *vop_find_vq(struct virtio_device *dev,
                return ERR_PTR(-ENOMEM);
        vdev->vr[index] = va;
        memset_io(va, 0x0, _vr_size);
-       vq = vring_new_virtqueue(
-                               index,
-                               le16_to_cpu(config.num), MIC_VIRTIO_RING_ALIGN,
-                               dev,
-                               false,
-                               ctx,
-                               (void __force *)va, vop_notify, callback, name);
-       if (!vq) {
-               err = -ENOMEM;
-               goto unmap;
-       }
+
        info = va + _vr_size;
        magic = ioread32(&info->magic);
 
@@ -341,18 +352,27 @@ static struct virtqueue *vop_find_vq(struct virtio_device *dev,
                goto unmap;
        }
 
-       /* Allocate and reassign used ring now */
        vdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 +
                                             sizeof(struct vring_used_elem) *
                                             le16_to_cpu(config.num));
        used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
                                        get_order(vdev->used_size[index]));
+       vdev->used_virt[index] = used;
        if (!used) {
                err = -ENOMEM;
                dev_err(_vop_dev(vdev), "%s %d err %d\n",
                        __func__, __LINE__, err);
-               goto del_vq;
+               goto unmap;
+       }
+
+       vq = vop_new_virtqueue(index, le16_to_cpu(config.num), dev, ctx,
+                              (void __force *)va, vop_notify, callback,
+                              name, used);
+       if (!vq) {
+               err = -ENOMEM;
+               goto free_used;
        }
+
        vdev->used[index] = dma_map_single(&vpdev->dev, used,
                                            vdev->used_size[index],
                                            DMA_BIDIRECTIONAL);
@@ -360,26 +380,17 @@ static struct virtqueue *vop_find_vq(struct virtio_device *dev,
                err = -ENOMEM;
                dev_err(_vop_dev(vdev), "%s %d err %d\n",
                        __func__, __LINE__, err);
-               goto free_used;
+               goto del_vq;
        }
        writeq(vdev->used[index], &vqconfig->used_address);
-       /*
-        * To reassign the used ring here we are directly accessing
-        * struct vring_virtqueue which is a private data structure
-        * in virtio_ring.c. At the minimum, a BUILD_BUG_ON() in
-        * vring_new_virtqueue() would ensure that
-        *  (&vq->vring == (struct vring *) (&vq->vq + 1));
-        */
-       vr = (struct vring *)(vq + 1);
-       vr->used = used;
 
        vq->priv = vdev;
        return vq;
+del_vq:
+       vring_del_virtqueue(vq);
 free_used:
        free_pages((unsigned long)used,
                   get_order(vdev->used_size[index]));
-del_vq:
-       vring_del_virtqueue(vq);
 unmap:
        vpdev->hw_ops->iounmap(vpdev, vdev->vr[index]);
        return ERR_PTR(err);
@@ -581,6 +592,8 @@ static int _vop_remove_device(struct mic_device_desc __iomem *d,
        int ret = -1;
 
        if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) {
+               struct device *dev = get_device(&vdev->vdev.dev);
+
                dev_dbg(&vpdev->dev,
                        "%s %d config_change %d type %d vdev %p\n",
                        __func__, __LINE__,
@@ -592,7 +605,7 @@ static int _vop_remove_device(struct mic_device_desc __iomem *d,
                iowrite8(-1, &dc->h2c_vdev_db);
                if (status & VIRTIO_CONFIG_S_DRIVER_OK)
                        wait_for_completion(&vdev->reset_done);
-               put_device(&vdev->vdev.dev);
+               put_device(dev);
                iowrite8(1, &dc->guest_ack);
                dev_dbg(&vpdev->dev, "%s %d guest_ack %d\n",
                        __func__, __LINE__, ioread8(&dc->guest_ack));
index aef1185..14f3fdb 100644 (file)
@@ -2112,7 +2112,7 @@ static void mmc_blk_mq_req_done(struct mmc_request *mrq)
                if (waiting)
                        wake_up(&mq->wait);
                else
-                       kblockd_schedule_work(&mq->complete_work);
+                       queue_work(mq->card->complete_wq, &mq->complete_work);
 
                return;
        }
@@ -2924,6 +2924,13 @@ static int mmc_blk_probe(struct mmc_card *card)
 
        mmc_fixup_device(card, mmc_blk_fixups);
 
+       card->complete_wq = alloc_workqueue("mmc_complete",
+                                       WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
+       if (unlikely(!card->complete_wq)) {
+               pr_err("Failed to create mmc completion workqueue");
+               return -ENOMEM;
+       }
+
        md = mmc_blk_alloc(card);
        if (IS_ERR(md))
                return PTR_ERR(md);
@@ -2987,6 +2994,7 @@ static void mmc_blk_remove(struct mmc_card *card)
        pm_runtime_put_noidle(&card->dev);
        mmc_blk_remove_req(md);
        dev_set_drvdata(&card->dev, NULL);
+       destroy_workqueue(card->complete_wq);
 }
 
 static int _mmc_blk_suspend(struct mmc_card *card)
index 5029352..c9e7aa5 100644 (file)
@@ -1431,6 +1431,8 @@ static int bcm2835_probe(struct platform_device *pdev)
 
 err:
        dev_dbg(dev, "%s -> err %d\n", __func__, ret);
+       if (host->dma_chan_rxtx)
+               dma_release_channel(host->dma_chan_rxtx);
        mmc_free_host(mmc);
 
        return ret;
index f19ec60..2eba507 100644 (file)
@@ -1338,7 +1338,8 @@ static int meson_mmc_probe(struct platform_device *pdev)
               host->regs + SD_EMMC_IRQ_EN);
 
        ret = request_threaded_irq(host->irq, meson_mmc_irq,
-                       meson_mmc_irq_thread, IRQF_SHARED, NULL, host);
+                                  meson_mmc_irq_thread, IRQF_SHARED,
+                                  dev_name(&pdev->dev), host);
        if (ret)
                goto err_init_clk;
 
index 8afeaf8..833ef05 100644 (file)
@@ -846,7 +846,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
 
        if (timing == MMC_TIMING_MMC_HS400 &&
            host->dev_comp->hs400_tune)
-               sdr_set_field(host->base + PAD_CMD_TUNE,
+               sdr_set_field(host->base + tune_reg,
                              MSDC_PAD_TUNE_CMDRRDLY,
                              host->hs400_cmd_int_delay);
        dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->mmc->actual_clock,
index 279e326..70fadc9 100644 (file)
@@ -1399,13 +1399,37 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
        mmc->caps              |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
                                  MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ;
 
-       if (host->cfg->clk_delays || host->use_new_timings)
+       /*
+        * Some H5 devices do not have signal traces precise enough to
+        * use HS DDR mode for their eMMC chips.
+        *
+        * We still enable HS DDR modes for all the other controller
+        * variants that support them.
+        */
+       if ((host->cfg->clk_delays || host->use_new_timings) &&
+           !of_device_is_compatible(pdev->dev.of_node,
+                                    "allwinner,sun50i-h5-emmc"))
                mmc->caps      |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
 
        ret = mmc_of_parse(mmc);
        if (ret)
                goto error_free_dma;
 
+       /*
+        * If we don't support delay chains in the SoC, we can't use any
+        * of the higher speed modes. Mask them out in case the device
+        * tree specifies the properties for them, which gets added to
+        * the caps by mmc_of_parse() above.
+        */
+       if (!(host->cfg->clk_delays || host->use_new_timings)) {
+               mmc->caps &= ~(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR |
+                              MMC_CAP_1_2V_DDR | MMC_CAP_UHS);
+               mmc->caps2 &= ~MMC_CAP2_HS200;
+       }
+
+       /* TODO: This driver doesn't support HS400 mode yet */
+       mmc->caps2 &= ~MMC_CAP2_HS400;
+
        ret = sunxi_mmc_init_host(host);
        if (ret)
                goto error_free_dma;
index 60104e1..37f174c 100644 (file)
@@ -480,6 +480,10 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent,
                /* let's register it anyway to preserve ordering */
                slave->offset = 0;
                slave->mtd.size = 0;
+
+               /* Initialize ->erasesize to make add_mtd_device() happy. */
+               slave->mtd.erasesize = parent->erasesize;
+
                printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
                        part->name);
                goto out_register;
@@ -632,7 +636,6 @@ err_remove_part:
        mutex_unlock(&mtd_partitions_mutex);
 
        free_partition(new);
-       pr_info("%s:%i\n", __func__, __LINE__);
 
        return ret;
 }
index bd4cfac..a4768df 100644 (file)
@@ -155,9 +155,10 @@ int gpmi_init(struct gpmi_nand_data *this)
 
        /*
         * Reset BCH here, too. We got failures otherwise :(
-        * See later BCH reset for explanation of MX23 handling
+        * See later BCH reset for explanation of MX23 and MX28 handling
         */
-       ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this));
+       ret = gpmi_reset_block(r->bch_regs,
+                              GPMI_IS_MX23(this) || GPMI_IS_MX28(this));
        if (ret)
                goto err_out;
 
@@ -263,12 +264,10 @@ int bch_set_geometry(struct gpmi_nand_data *this)
        /*
        * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this
        * chip, otherwise it will lock up. So we skip resetting BCH on the MX23.
-       * On the other hand, the MX28 needs the reset, because one case has been
-       * seen where the BCH produced ECC errors constantly after 10000
-       * consecutive reboots. The latter case has not been seen on the MX23
-       * yet, still we don't know if it could happen there as well.
+       * and MX28.
        */
-       ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MX23(this));
+       ret = gpmi_reset_block(r->bch_regs,
+                              GPMI_IS_MX23(this) || GPMI_IS_MX28(this));
        if (ret)
                goto err_out;
 
index cca4b24..839494a 100644 (file)
@@ -410,6 +410,7 @@ static int nand_check_wp(struct nand_chip *chip)
 
 /**
  * nand_fill_oob - [INTERN] Transfer client buffer to oob
+ * @chip: NAND chip object
  * @oob: oob data buffer
  * @len: oob data write length
  * @ops: oob ops structure
index 1b722fe..19a2b56 100644 (file)
@@ -158,7 +158,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
 
 /**
  * read_bbt - [GENERIC] Read the bad block table starting from page
- * @chip: NAND chip object
+ * @this: NAND chip object
  * @buf: temporary buffer
  * @page: the starting page
  * @num: the number of bbt descriptors to read
index 479c2f2..fa87ae2 100644 (file)
@@ -304,24 +304,30 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
        struct nand_device *nand = spinand_to_nand(spinand);
        struct mtd_info *mtd = nanddev_to_mtd(nand);
        struct nand_page_io_req adjreq = *req;
-       unsigned int nbytes = 0;
-       void *buf = NULL;
+       void *buf = spinand->databuf;
+       unsigned int nbytes;
        u16 column = 0;
        int ret;
 
-       memset(spinand->databuf, 0xff,
-              nanddev_page_size(nand) +
-              nanddev_per_page_oobsize(nand));
+       /*
+        * Looks like PROGRAM LOAD (AKA write cache) does not necessarily reset
+        * the cache content to 0xFF (depends on vendor implementation), so we
+        * must fill the page cache entirely even if we only want to program
+        * the data portion of the page, otherwise we might corrupt the BBM or
+        * user data previously programmed in OOB area.
+        */
+       nbytes = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
+       memset(spinand->databuf, 0xff, nbytes);
+       adjreq.dataoffs = 0;
+       adjreq.datalen = nanddev_page_size(nand);
+       adjreq.databuf.out = spinand->databuf;
+       adjreq.ooblen = nanddev_per_page_oobsize(nand);
+       adjreq.ooboffs = 0;
+       adjreq.oobbuf.out = spinand->oobbuf;
 
-       if (req->datalen) {
+       if (req->datalen)
                memcpy(spinand->databuf + req->dataoffs, req->databuf.out,
                       req->datalen);
-               adjreq.dataoffs = 0;
-               adjreq.datalen = nanddev_page_size(nand);
-               adjreq.databuf.out = spinand->databuf;
-               nbytes = adjreq.datalen;
-               buf = spinand->databuf;
-       }
 
        if (req->ooblen) {
                if (req->mode == MTD_OPS_AUTO_OOB)
@@ -332,14 +338,6 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
                else
                        memcpy(spinand->oobbuf + req->ooboffs, req->oobbuf.out,
                               req->ooblen);
-
-               adjreq.ooblen = nanddev_per_page_oobsize(nand);
-               adjreq.ooboffs = 0;
-               nbytes += nanddev_per_page_oobsize(nand);
-               if (!buf) {
-                       buf = spinand->oobbuf;
-                       column = nanddev_page_size(nand);
-               }
        }
 
        spinand_cache_op_adjust_colum(spinand, &adjreq, &column);
@@ -370,8 +368,8 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
 
                /*
                 * We need to use the RANDOM LOAD CACHE operation if there's
-                * more than one iteration, because the LOAD operation resets
-                * the cache to 0xff.
+                * more than one iteration, because the LOAD operation might
+                * reset the cache to 0xff.
                 */
                if (nbytes) {
                        column = op.addr.val;
@@ -1018,11 +1016,11 @@ static int spinand_init(struct spinand_device *spinand)
        for (i = 0; i < nand->memorg.ntargets; i++) {
                ret = spinand_select_target(spinand, i);
                if (ret)
-                       goto err_free_bufs;
+                       goto err_manuf_cleanup;
 
                ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED);
                if (ret)
-                       goto err_free_bufs;
+                       goto err_manuf_cleanup;
        }
 
        ret = nanddev_init(nand, &spinand_ops, THIS_MODULE);
index edb1c02..6210757 100644 (file)
@@ -145,13 +145,16 @@ config MACVTAP
          To compile this driver as a module, choose M here: the module
          will be called macvtap.
 
+config IPVLAN_L3S
+       depends on NETFILTER
+       depends on IPVLAN
+       def_bool y
+       select NET_L3_MASTER_DEV
 
 config IPVLAN
     tristate "IP-VLAN support"
     depends on INET
     depends on IPV6 || !IPV6
-    depends on NETFILTER
-    select NET_L3_MASTER_DEV
     ---help---
       This allows one to create virtual devices off of a main interface
       and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
@@ -197,9 +200,9 @@ config VXLAN
 
 config GENEVE
        tristate "Generic Network Virtualization Encapsulation"
-       depends on INET && NET_UDP_TUNNEL
+       depends on INET
        depends on IPV6 || !IPV6
-       select NET_IP_TUNNEL
+       select NET_UDP_TUNNEL
        select GRO_CELLS
        ---help---
          This allows one to create geneve virtual interfaces that provide
index f90bb72..b3c63d2 100644 (file)
@@ -301,7 +301,7 @@ static int __init cops_probe1(struct net_device *dev, int ioaddr)
                        dev->irq = cops_irq(ioaddr, board);
                        if (dev->irq)
                                break;
-                       /* No IRQ found on this port, fallthrough */
+                       /* fall through - Once no IRQ found on this port. */
                case 1:
                        retval = -EINVAL;
                        goto err_out;
index 4d5d01c..da1fc17 100644 (file)
@@ -1375,6 +1375,7 @@ static int bond_option_slaves_set(struct bonding *bond,
        sscanf(newval->string, "%16s", command); /* IFNAMSIZ*/
        ifname = command + 1;
        if ((strlen(command) <= 1) ||
+           (command[0] != '+' && command[0] != '-') ||
            !dev_valid_name(ifname))
                goto err_no_cmd;
 
@@ -1398,6 +1399,7 @@ static int bond_option_slaves_set(struct bonding *bond,
                break;
 
        default:
+               /* should not run here. */
                goto err_no_cmd;
        }
 
index a0f954f..44e6c7b 100644 (file)
@@ -257,10 +257,7 @@ static int handle_tx(struct ser_device *ser)
                if (skb->len == 0) {
                        struct sk_buff *tmp = skb_dequeue(&ser->head);
                        WARN_ON(tmp != skb);
-                       if (in_interrupt())
-                               dev_kfree_skb_irq(skb);
-                       else
-                               kfree_skb(skb);
+                       dev_consume_skb_any(skb);
                }
        }
        /* Send flow off if queue is empty */
index d28a139..7608bc3 100644 (file)
@@ -73,35 +73,37 @@ MODULE_PARM_DESC(spi_down_tail_align, "SPI downlink tail alignment.");
 #define LOW_WATER_MARK   100
 #define HIGH_WATER_MARK  (LOW_WATER_MARK*5)
 
-#ifdef CONFIG_UML
+#ifndef CONFIG_HAS_DMA
 
 /*
  * We sometimes use UML for debugging, but it cannot handle
  * dma_alloc_coherent so we have to wrap it.
  */
-static inline void *dma_alloc(dma_addr_t *daddr)
+static inline void *dma_alloc(struct cfspi *cfspi, dma_addr_t *daddr)
 {
        return kmalloc(SPI_DMA_BUF_LEN, GFP_KERNEL);
 }
 
-static inline void dma_free(void *cpu_addr, dma_addr_t handle)
+static inline void dma_free(struct cfspi *cfspi, void *cpu_addr,
+               dma_addr_t handle)
 {
        kfree(cpu_addr);
 }
 
 #else
 
-static inline void *dma_alloc(dma_addr_t *daddr)
+static inline void *dma_alloc(struct cfspi *cfspi, dma_addr_t *daddr)
 {
-       return dma_alloc_coherent(NULL, SPI_DMA_BUF_LEN, daddr,
+       return dma_alloc_coherent(&cfspi->pdev->dev, SPI_DMA_BUF_LEN, daddr,
                                GFP_KERNEL);
 }
 
-static inline void dma_free(void *cpu_addr, dma_addr_t handle)
+static inline void dma_free(struct cfspi *cfspi, void *cpu_addr,
+               dma_addr_t handle)
 {
-       dma_free_coherent(NULL, SPI_DMA_BUF_LEN, cpu_addr, handle);
+       dma_free_coherent(&cfspi->pdev->dev, SPI_DMA_BUF_LEN, cpu_addr, handle);
 }
-#endif /* CONFIG_UML */
+#endif /* CONFIG_HAS_DMA */
 
 #ifdef CONFIG_DEBUG_FS
 
@@ -610,13 +612,13 @@ static int cfspi_init(struct net_device *dev)
        }
 
        /* Allocate DMA buffers. */
-       cfspi->xfer.va_tx[0] = dma_alloc(&cfspi->xfer.pa_tx[0]);
+       cfspi->xfer.va_tx[0] = dma_alloc(cfspi, &cfspi->xfer.pa_tx[0]);
        if (!cfspi->xfer.va_tx[0]) {
                res = -ENODEV;
                goto err_dma_alloc_tx_0;
        }
 
-       cfspi->xfer.va_rx = dma_alloc(&cfspi->xfer.pa_rx);
+       cfspi->xfer.va_rx = dma_alloc(cfspi, &cfspi->xfer.pa_rx);
 
        if (!cfspi->xfer.va_rx) {
                res = -ENODEV;
@@ -665,9 +667,9 @@ static int cfspi_init(struct net_device *dev)
        return 0;
 
  err_create_wq:
-       dma_free(cfspi->xfer.va_rx, cfspi->xfer.pa_rx);
+       dma_free(cfspi, cfspi->xfer.va_rx, cfspi->xfer.pa_rx);
  err_dma_alloc_rx:
-       dma_free(cfspi->xfer.va_tx[0], cfspi->xfer.pa_tx[0]);
+       dma_free(cfspi, cfspi->xfer.va_tx[0], cfspi->xfer.pa_tx[0]);
  err_dma_alloc_tx_0:
        return res;
 }
@@ -683,8 +685,8 @@ static void cfspi_uninit(struct net_device *dev)
 
        cfspi->ndev = NULL;
        /* Free DMA buffers. */
-       dma_free(cfspi->xfer.va_rx, cfspi->xfer.pa_rx);
-       dma_free(cfspi->xfer.va_tx[0], cfspi->xfer.pa_tx[0]);
+       dma_free(cfspi, cfspi->xfer.va_rx, cfspi->xfer.pa_rx);
+       dma_free(cfspi, cfspi->xfer.va_tx[0], cfspi->xfer.pa_tx[0]);
        set_bit(SPI_TERMINATE, &cfspi->state);
        wake_up_interruptible(&cfspi->wait);
        destroy_workqueue(cfspi->wq);
index 90f5142..d9c56a7 100644 (file)
@@ -511,9 +511,6 @@ static void b53_srab_prepare_irq(struct platform_device *pdev)
        /* Clear all pending interrupts */
        writel(0xffffffff, priv->regs + B53_SRAB_INTR);
 
-       if (dev->pdata && dev->pdata->chip_id != BCM58XX_DEVICE_ID)
-               return;
-
        for (i = 0; i < B53_N_PORTS; i++) {
                port = &priv->port_intrs[i];
 
index 361fbde..98696a8 100644 (file)
@@ -690,7 +690,7 @@ static int bcm_sf2_sw_suspend(struct dsa_switch *ds)
         * port, the other ones have already been disabled during
         * bcm_sf2_sw_setup
         */
-       for (port = 0; port < DSA_MAX_PORTS; port++) {
+       for (port = 0; port < ds->num_ports; port++) {
                if (dsa_is_user_port(ds, port) || dsa_is_cpu_port(ds, port))
                        bcm_sf2_port_disable(ds, port, NULL);
        }
@@ -894,12 +894,44 @@ static const struct b53_io_ops bcm_sf2_io_ops = {
        .write64 = bcm_sf2_core_write64,
 };
 
+static void bcm_sf2_sw_get_strings(struct dsa_switch *ds, int port,
+                                  u32 stringset, uint8_t *data)
+{
+       int cnt = b53_get_sset_count(ds, port, stringset);
+
+       b53_get_strings(ds, port, stringset, data);
+       bcm_sf2_cfp_get_strings(ds, port, stringset,
+                               data + cnt * ETH_GSTRING_LEN);
+}
+
+static void bcm_sf2_sw_get_ethtool_stats(struct dsa_switch *ds, int port,
+                                        uint64_t *data)
+{
+       int cnt = b53_get_sset_count(ds, port, ETH_SS_STATS);
+
+       b53_get_ethtool_stats(ds, port, data);
+       bcm_sf2_cfp_get_ethtool_stats(ds, port, data + cnt);
+}
+
+static int bcm_sf2_sw_get_sset_count(struct dsa_switch *ds, int port,
+                                    int sset)
+{
+       int cnt = b53_get_sset_count(ds, port, sset);
+
+       if (cnt < 0)
+               return cnt;
+
+       cnt += bcm_sf2_cfp_get_sset_count(ds, port, sset);
+
+       return cnt;
+}
+
 static const struct dsa_switch_ops bcm_sf2_ops = {
        .get_tag_protocol       = b53_get_tag_protocol,
        .setup                  = bcm_sf2_sw_setup,
-       .get_strings            = b53_get_strings,
-       .get_ethtool_stats      = b53_get_ethtool_stats,
-       .get_sset_count         = b53_get_sset_count,
+       .get_strings            = bcm_sf2_sw_get_strings,
+       .get_ethtool_stats      = bcm_sf2_sw_get_ethtool_stats,
+       .get_sset_count         = bcm_sf2_sw_get_sset_count,
        .get_ethtool_phy_stats  = b53_get_ethtool_phy_stats,
        .get_phy_flags          = bcm_sf2_sw_get_phy_flags,
        .phylink_validate       = bcm_sf2_sw_validate,
@@ -1062,7 +1094,6 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
        dev_set_drvdata(&pdev->dev, priv);
 
        spin_lock_init(&priv->indir_lock);
-       mutex_init(&priv->stats_mutex);
        mutex_init(&priv->cfp.lock);
        INIT_LIST_HEAD(&priv->cfp.rules_list);
 
index faaef32..eb3655b 100644 (file)
@@ -87,9 +87,6 @@ struct bcm_sf2_priv {
        /* Backing b53_device */
        struct b53_device               *dev;
 
-       /* Mutex protecting access to the MIB counters */
-       struct mutex                    stats_mutex;
-
        struct bcm_sf2_hw_params        hw_params;
 
        struct bcm_sf2_port_status      port_sts[DSA_MAX_PORTS];
@@ -216,5 +213,10 @@ int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,
 int bcm_sf2_cfp_rst(struct bcm_sf2_priv *priv);
 void bcm_sf2_cfp_exit(struct dsa_switch *ds);
 int bcm_sf2_cfp_resume(struct dsa_switch *ds);
+void bcm_sf2_cfp_get_strings(struct dsa_switch *ds, int port,
+                            u32 stringset, uint8_t *data);
+void bcm_sf2_cfp_get_ethtool_stats(struct dsa_switch *ds, int port,
+                                  uint64_t *data);
+int bcm_sf2_cfp_get_sset_count(struct dsa_switch *ds, int port, int sset);
 
 #endif /* __BCM_SF2_H */
index e14663a..e6234d2 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/netdevice.h>
 #include <net/dsa.h>
 #include <linux/bitmap.h>
+#include <net/flow_offload.h>
 
 #include "bcm_sf2.h"
 #include "bcm_sf2_regs.h"
@@ -212,6 +213,7 @@ static inline unsigned int bcm_sf2_cfp_rule_size(struct bcm_sf2_priv *priv)
 
 static int bcm_sf2_cfp_act_pol_set(struct bcm_sf2_priv *priv,
                                   unsigned int rule_index,
+                                  int src_port,
                                   unsigned int port_num,
                                   unsigned int queue_num,
                                   bool fwd_map_change)
@@ -229,6 +231,10 @@ static int bcm_sf2_cfp_act_pol_set(struct bcm_sf2_priv *priv,
        else
                reg = 0;
 
+       /* Enable looping back to the original port */
+       if (src_port == port_num)
+               reg |= LOOP_BK_EN;
+
        core_writel(priv, reg, CORE_ACT_POL_DATA0);
 
        /* Set classification ID that needs to be put in Broadcom tag */
@@ -257,7 +263,8 @@ static int bcm_sf2_cfp_act_pol_set(struct bcm_sf2_priv *priv,
 }
 
 static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv,
-                                  struct ethtool_tcpip4_spec *v4_spec,
+                                  struct flow_dissector_key_ipv4_addrs *addrs,
+                                  struct flow_dissector_key_ports *ports,
                                   unsigned int slice_num,
                                   bool mask)
 {
@@ -278,7 +285,7 @@ static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv,
         * UDF_n_A6             [23:8]
         * UDF_n_A5             [7:0]
         */
-       reg = be16_to_cpu(v4_spec->pdst) >> 8;
+       reg = be16_to_cpu(ports->dst) >> 8;
        if (mask)
                offset = CORE_CFP_MASK_PORT(3);
        else
@@ -289,9 +296,9 @@ static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv,
         * UDF_n_A4             [23:8]
         * UDF_n_A3             [7:0]
         */
-       reg = (be16_to_cpu(v4_spec->pdst) & 0xff) << 24 |
-             (u32)be16_to_cpu(v4_spec->psrc) << 8 |
-             (be32_to_cpu(v4_spec->ip4dst) & 0x0000ff00) >> 8;
+       reg = (be16_to_cpu(ports->dst) & 0xff) << 24 |
+             (u32)be16_to_cpu(ports->src) << 8 |
+             (be32_to_cpu(addrs->dst) & 0x0000ff00) >> 8;
        if (mask)
                offset = CORE_CFP_MASK_PORT(2);
        else
@@ -302,9 +309,9 @@ static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv,
         * UDF_n_A2             [23:8]
         * UDF_n_A1             [7:0]
         */
-       reg = (u32)(be32_to_cpu(v4_spec->ip4dst) & 0xff) << 24 |
-             (u32)(be32_to_cpu(v4_spec->ip4dst) >> 16) << 8 |
-             (be32_to_cpu(v4_spec->ip4src) & 0x0000ff00) >> 8;
+       reg = (u32)(be32_to_cpu(addrs->dst) & 0xff) << 24 |
+             (u32)(be32_to_cpu(addrs->dst) >> 16) << 8 |
+             (be32_to_cpu(addrs->src) & 0x0000ff00) >> 8;
        if (mask)
                offset = CORE_CFP_MASK_PORT(1);
        else
@@ -317,8 +324,8 @@ static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv,
         * Slice ID             [3:2]
         * Slice valid          [1:0]
         */
-       reg = (u32)(be32_to_cpu(v4_spec->ip4src) & 0xff) << 24 |
-             (u32)(be32_to_cpu(v4_spec->ip4src) >> 16) << 8 |
+       reg = (u32)(be32_to_cpu(addrs->src) & 0xff) << 24 |
+             (u32)(be32_to_cpu(addrs->src) >> 16) << 8 |
              SLICE_NUM(slice_num) | SLICE_VALID;
        if (mask)
                offset = CORE_CFP_MASK_PORT(0);
@@ -332,9 +339,13 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
                                     unsigned int queue_num,
                                     struct ethtool_rx_flow_spec *fs)
 {
-       struct ethtool_tcpip4_spec *v4_spec, *v4_m_spec;
+       struct ethtool_rx_flow_spec_input input = {};
        const struct cfp_udf_layout *layout;
        unsigned int slice_num, rule_index;
+       struct ethtool_rx_flow_rule *flow;
+       struct flow_match_ipv4_addrs ipv4;
+       struct flow_match_ports ports;
+       struct flow_match_ip ip;
        u8 ip_proto, ip_frag;
        u8 num_udf;
        u32 reg;
@@ -343,13 +354,9 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
        switch (fs->flow_type & ~FLOW_EXT) {
        case TCP_V4_FLOW:
                ip_proto = IPPROTO_TCP;
-               v4_spec = &fs->h_u.tcp_ip4_spec;
-               v4_m_spec = &fs->m_u.tcp_ip4_spec;
                break;
        case UDP_V4_FLOW:
                ip_proto = IPPROTO_UDP;
-               v4_spec = &fs->h_u.udp_ip4_spec;
-               v4_m_spec = &fs->m_u.udp_ip4_spec;
                break;
        default:
                return -EINVAL;
@@ -367,11 +374,22 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
        if (rule_index > bcm_sf2_cfp_rule_size(priv))
                return -ENOSPC;
 
+       input.fs = fs;
+       flow = ethtool_rx_flow_rule_create(&input);
+       if (IS_ERR(flow))
+               return PTR_ERR(flow);
+
+       flow_rule_match_ipv4_addrs(flow->rule, &ipv4);
+       flow_rule_match_ports(flow->rule, &ports);
+       flow_rule_match_ip(flow->rule, &ip);
+
        layout = &udf_tcpip4_layout;
        /* We only use one UDF slice for now */
        slice_num = bcm_sf2_get_slice_number(layout, 0);
-       if (slice_num == UDF_NUM_SLICES)
-               return -EINVAL;
+       if (slice_num == UDF_NUM_SLICES) {
+               ret = -EINVAL;
+               goto out_err_flow_rule;
+       }
 
        num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices);
 
@@ -398,7 +416,7 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
         * Reserved             [1]
         * UDF_Valid[8]         [0]
         */
-       core_writel(priv, v4_spec->tos << IPTOS_SHIFT |
+       core_writel(priv, ip.key->tos << IPTOS_SHIFT |
                    ip_proto << IPPROTO_SHIFT | ip_frag << IP_FRAG_SHIFT |
                    udf_upper_bits(num_udf),
                    CORE_CFP_DATA_PORT(6));
@@ -417,8 +435,8 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
        core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5));
 
        /* Program the match and the mask */
-       bcm_sf2_cfp_slice_ipv4(priv, v4_spec, slice_num, false);
-       bcm_sf2_cfp_slice_ipv4(priv, v4_m_spec, SLICE_NUM_MASK, true);
+       bcm_sf2_cfp_slice_ipv4(priv, ipv4.key, ports.key, slice_num, false);
+       bcm_sf2_cfp_slice_ipv4(priv, ipv4.mask, ports.mask, SLICE_NUM_MASK, true);
 
        /* Insert into TCAM now */
        bcm_sf2_cfp_rule_addr_set(priv, rule_index);
@@ -426,14 +444,14 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
        ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);
        if (ret) {
                pr_err("TCAM entry at addr %d failed\n", rule_index);
-               return ret;
+               goto out_err_flow_rule;
        }
 
        /* Insert into Action and policer RAMs now */
-       ret = bcm_sf2_cfp_act_pol_set(priv, rule_index, port_num,
+       ret = bcm_sf2_cfp_act_pol_set(priv, rule_index, port, port_num,
                                      queue_num, true);
        if (ret)
-               return ret;
+               goto out_err_flow_rule;
 
        /* Turn on CFP for this rule now */
        reg = core_readl(priv, CORE_CFP_CTL_REG);
@@ -446,6 +464,10 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
        fs->location = rule_index;
 
        return 0;
+
+out_err_flow_rule:
+       ethtool_rx_flow_rule_destroy(flow);
+       return ret;
 }
 
 static void bcm_sf2_cfp_slice_ipv6(struct bcm_sf2_priv *priv,
@@ -581,9 +603,12 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
                                     unsigned int queue_num,
                                     struct ethtool_rx_flow_spec *fs)
 {
-       struct ethtool_tcpip6_spec *v6_spec, *v6_m_spec;
+       struct ethtool_rx_flow_spec_input input = {};
        unsigned int slice_num, rule_index[2];
        const struct cfp_udf_layout *layout;
+       struct ethtool_rx_flow_rule *flow;
+       struct flow_match_ipv6_addrs ipv6;
+       struct flow_match_ports ports;
        u8 ip_proto, ip_frag;
        int ret = 0;
        u8 num_udf;
@@ -592,13 +617,9 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
        switch (fs->flow_type & ~FLOW_EXT) {
        case TCP_V6_FLOW:
                ip_proto = IPPROTO_TCP;
-               v6_spec = &fs->h_u.tcp_ip6_spec;
-               v6_m_spec = &fs->m_u.tcp_ip6_spec;
                break;
        case UDP_V6_FLOW:
                ip_proto = IPPROTO_UDP;
-               v6_spec = &fs->h_u.udp_ip6_spec;
-               v6_m_spec = &fs->m_u.udp_ip6_spec;
                break;
        default:
                return -EINVAL;
@@ -645,6 +666,15 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
                goto out_err;
        }
 
+       input.fs = fs;
+       flow = ethtool_rx_flow_rule_create(&input);
+       if (IS_ERR(flow)) {
+               ret = PTR_ERR(flow);
+               goto out_err;
+       }
+       flow_rule_match_ipv6_addrs(flow->rule, &ipv6);
+       flow_rule_match_ports(flow->rule, &ports);
+
        /* Apply the UDF layout for this filter */
        bcm_sf2_cfp_udf_set(priv, layout, slice_num);
 
@@ -688,10 +718,10 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
        core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5));
 
        /* Slice the IPv6 source address and port */
-       bcm_sf2_cfp_slice_ipv6(priv, v6_spec->ip6src, v6_spec->psrc,
-                               slice_num, false);
-       bcm_sf2_cfp_slice_ipv6(priv, v6_m_spec->ip6src, v6_m_spec->psrc,
-                               SLICE_NUM_MASK, true);
+       bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->src.in6_u.u6_addr32,
+                              ports.key->src, slice_num, false);
+       bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->src.in6_u.u6_addr32,
+                              ports.mask->src, SLICE_NUM_MASK, true);
 
        /* Insert into TCAM now because we need to insert a second rule */
        bcm_sf2_cfp_rule_addr_set(priv, rule_index[0]);
@@ -699,20 +729,20 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
        ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);
        if (ret) {
                pr_err("TCAM entry at addr %d failed\n", rule_index[0]);
-               goto out_err;
+               goto out_err_flow_rule;
        }
 
        /* Insert into Action and policer RAMs now */
-       ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[0], port_num,
+       ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[0], port, port_num,
                                      queue_num, false);
        if (ret)
-               goto out_err;
+               goto out_err_flow_rule;
 
        /* Now deal with the second slice to chain this rule */
        slice_num = bcm_sf2_get_slice_number(layout, slice_num + 1);
        if (slice_num == UDF_NUM_SLICES) {
                ret = -EINVAL;
-               goto out_err;
+               goto out_err_flow_rule;
        }
 
        num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices);
@@ -748,10 +778,10 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
        /* Mask all */
        core_writel(priv, 0, CORE_CFP_MASK_PORT(5));
 
-       bcm_sf2_cfp_slice_ipv6(priv, v6_spec->ip6dst, v6_spec->pdst, slice_num,
-                              false);
-       bcm_sf2_cfp_slice_ipv6(priv, v6_m_spec->ip6dst, v6_m_spec->pdst,
-                              SLICE_NUM_MASK, true);
+       bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->dst.in6_u.u6_addr32,
+                              ports.key->dst, slice_num, false);
+       bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->dst.in6_u.u6_addr32,
+                              ports.key->dst, SLICE_NUM_MASK, true);
 
        /* Insert into TCAM now */
        bcm_sf2_cfp_rule_addr_set(priv, rule_index[1]);
@@ -759,16 +789,16 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
        ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);
        if (ret) {
                pr_err("TCAM entry at addr %d failed\n", rule_index[1]);
-               goto out_err;
+               goto out_err_flow_rule;
        }
 
        /* Insert into Action and policer RAMs now, set chain ID to
         * the one we are chained to
         */
-       ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[1], port_num,
+       ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[1], port, port_num,
                                      queue_num, true);
        if (ret)
-               goto out_err;
+               goto out_err_flow_rule;
 
        /* Turn on CFP for this rule now */
        reg = core_readl(priv, CORE_CFP_CTL_REG);
@@ -784,6 +814,8 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
 
        return ret;
 
+out_err_flow_rule:
+       ethtool_rx_flow_rule_destroy(flow);
 out_err:
        clear_bit(rule_index[1], priv->cfp.used);
        return ret;
@@ -1169,3 +1201,91 @@ int bcm_sf2_cfp_resume(struct dsa_switch *ds)
 
        return ret;
 }
+
+static const struct bcm_sf2_cfp_stat {
+       unsigned int offset;
+       unsigned int ram_loc;
+       const char *name;
+} bcm_sf2_cfp_stats[] = {
+       {
+               .offset = CORE_STAT_GREEN_CNTR,
+               .ram_loc = GREEN_STAT_RAM,
+               .name = "Green"
+       },
+       {
+               .offset = CORE_STAT_YELLOW_CNTR,
+               .ram_loc = YELLOW_STAT_RAM,
+               .name = "Yellow"
+       },
+       {
+               .offset = CORE_STAT_RED_CNTR,
+               .ram_loc = RED_STAT_RAM,
+               .name = "Red"
+       },
+};
+
+void bcm_sf2_cfp_get_strings(struct dsa_switch *ds, int port,
+                            u32 stringset, uint8_t *data)
+{
+       struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+       unsigned int s = ARRAY_SIZE(bcm_sf2_cfp_stats);
+       char buf[ETH_GSTRING_LEN];
+       unsigned int i, j, iter;
+
+       if (stringset != ETH_SS_STATS)
+               return;
+
+       for (i = 1; i < priv->num_cfp_rules; i++) {
+               for (j = 0; j < s; j++) {
+                       snprintf(buf, sizeof(buf),
+                                "CFP%03d_%sCntr",
+                                i, bcm_sf2_cfp_stats[j].name);
+                       iter = (i - 1) * s + j;
+                       strlcpy(data + iter * ETH_GSTRING_LEN,
+                               buf, ETH_GSTRING_LEN);
+               }
+       }
+}
+
+void bcm_sf2_cfp_get_ethtool_stats(struct dsa_switch *ds, int port,
+                                  uint64_t *data)
+{
+       struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+       unsigned int s = ARRAY_SIZE(bcm_sf2_cfp_stats);
+       const struct bcm_sf2_cfp_stat *stat;
+       unsigned int i, j, iter;
+       struct cfp_rule *rule;
+       int ret;
+
+       mutex_lock(&priv->cfp.lock);
+       for (i = 1; i < priv->num_cfp_rules; i++) {
+               rule = bcm_sf2_cfp_rule_find(priv, port, i);
+               if (!rule)
+                       continue;
+
+               for (j = 0; j < s; j++) {
+                       stat = &bcm_sf2_cfp_stats[j];
+
+                       bcm_sf2_cfp_rule_addr_set(priv, i);
+                       ret = bcm_sf2_cfp_op(priv, stat->ram_loc | OP_SEL_READ);
+                       if (ret)
+                               continue;
+
+                       iter = (i - 1) * s + j;
+                       data[iter] = core_readl(priv, stat->offset);
+               }
+
+       }
+       mutex_unlock(&priv->cfp.lock);
+}
+
+int bcm_sf2_cfp_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+       struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+
+       if (sset != ETH_SS_STATS)
+               return 0;
+
+       /* 3 counters per CFP rules */
+       return (priv->num_cfp_rules - 1) * ARRAY_SIZE(bcm_sf2_cfp_stats);
+}
index 0a1e530..67f0562 100644 (file)
@@ -400,6 +400,10 @@ enum bcm_sf2_reg_offs {
 #define CORE_RATE_METER6               0x281e0
 #define  CIR_REF_CNT_MASK              0x7ffff
 
+#define CORE_STAT_GREEN_CNTR           0x28200
+#define CORE_STAT_YELLOW_CNTR          0x28210
+#define CORE_STAT_RED_CNTR             0x28220
+
 #define CORE_CFP_CTL_REG               0x28400
 #define  CFP_EN_MAP_MASK               0x1ff
 
index 816f34d..17482ae 100644 (file)
@@ -343,7 +343,7 @@ static int __init dsa_loop_init(void)
        unsigned int i;
 
        for (i = 0; i < NUM_FIXED_PHYS; i++)
-               phydevs[i] = fixed_phy_register(PHY_POLL, &status, -1, NULL);
+               phydevs[i] = fixed_phy_register(PHY_POLL, &status, NULL);
 
        return mdio_driver_register(&dsa_loop_drv);
 }
index 89ed059..674d77e 100644 (file)
@@ -397,6 +397,7 @@ static void ksz9477_port_stp_state_set(struct dsa_switch *ds, int port,
        struct ksz_port *p = &dev->ports[port];
        u8 data;
        int member = -1;
+       int forward = dev->member;
 
        ksz_pread8(dev, port, P_STP_CTRL, &data);
        data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
@@ -464,10 +465,10 @@ static void ksz9477_port_stp_state_set(struct dsa_switch *ds, int port,
        }
 
        /* When topology has changed the function ksz_update_port_member
-        * should be called to modify port forwarding behavior.  However
-        * as the offload_fwd_mark indication cannot be reported here
-        * the switch forwarding function is not enabled.
+        * should be called to modify port forwarding behavior.
         */
+       if (forward != dev->member)
+               ksz_update_port_member(dev, port);
 }
 
 static void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port)
index a8a2c72..c2b6150 100644 (file)
@@ -621,17 +621,19 @@ static void mt7530_adjust_link(struct dsa_switch *ds, int port,
        struct mt7530_priv *priv = ds->priv;
 
        if (phy_is_pseudo_fixed_link(phydev)) {
-               dev_dbg(priv->dev, "phy-mode for master device = %x\n",
-                       phydev->interface);
-
-               /* Setup TX circuit incluing relevant PAD and driving */
-               mt7530_pad_clk_setup(ds, phydev->interface);
-
-               /* Setup RX circuit, relevant PAD and driving on the host
-                * which must be placed after the setup on the device side is
-                * all finished.
-                */
-               mt7623_pad_clk_setup(ds);
+               if (priv->id == ID_MT7530) {
+                       dev_dbg(priv->dev, "phy-mode for master device = %x\n",
+                               phydev->interface);
+
+                       /* Setup TX circuit incluing relevant PAD and driving */
+                       mt7530_pad_clk_setup(ds, phydev->interface);
+
+                       /* Setup RX circuit, relevant PAD and driving on the
+                        * host which must be placed after the setup on the
+                        * device side is all finished.
+                        */
+                       mt7623_pad_clk_setup(ds);
+               }
        } else {
                u16 lcl_adv = 0, rmt_adv = 0;
                u8 flowctrl;
@@ -687,6 +689,10 @@ mt7530_cpu_port_enable(struct mt7530_priv *priv,
        /* Unknown unicast frame fordwarding to the cpu port */
        mt7530_set(priv, MT7530_MFC, UNU_FFP(BIT(port)));
 
+       /* Set CPU port number */
+       if (priv->id == ID_MT7621)
+               mt7530_rmw(priv, MT7530_MFC, CPU_MASK, CPU_EN | CPU_PORT(port));
+
        /* CPU port gets connected to all user ports of
         * the switch
         */
@@ -1219,24 +1225,27 @@ mt7530_setup(struct dsa_switch *ds)
         * as two netdev instances.
         */
        dn = ds->ports[MT7530_CPU_PORT].master->dev.of_node->parent;
-       priv->ethernet = syscon_node_to_regmap(dn);
-       if (IS_ERR(priv->ethernet))
-               return PTR_ERR(priv->ethernet);
 
-       regulator_set_voltage(priv->core_pwr, 1000000, 1000000);
-       ret = regulator_enable(priv->core_pwr);
-       if (ret < 0) {
-               dev_err(priv->dev,
-                       "Failed to enable core power: %d\n", ret);
-               return ret;
-       }
+       if (priv->id == ID_MT7530) {
+               priv->ethernet = syscon_node_to_regmap(dn);
+               if (IS_ERR(priv->ethernet))
+                       return PTR_ERR(priv->ethernet);
+
+               regulator_set_voltage(priv->core_pwr, 1000000, 1000000);
+               ret = regulator_enable(priv->core_pwr);
+               if (ret < 0) {
+                       dev_err(priv->dev,
+                               "Failed to enable core power: %d\n", ret);
+                       return ret;
+               }
 
-       regulator_set_voltage(priv->io_pwr, 3300000, 3300000);
-       ret = regulator_enable(priv->io_pwr);
-       if (ret < 0) {
-               dev_err(priv->dev, "Failed to enable io pwr: %d\n",
-                       ret);
-               return ret;
+               regulator_set_voltage(priv->io_pwr, 3300000, 3300000);
+               ret = regulator_enable(priv->io_pwr);
+               if (ret < 0) {
+                       dev_err(priv->dev, "Failed to enable io pwr: %d\n",
+                               ret);
+                       return ret;
+               }
        }
 
        /* Reset whole chip through gpio pin or memory-mapped registers for
@@ -1326,6 +1335,13 @@ static const struct dsa_switch_ops mt7530_switch_ops = {
        .port_vlan_del          = mt7530_port_vlan_del,
 };
 
+static const struct of_device_id mt7530_of_match[] = {
+       { .compatible = "mediatek,mt7621", .data = (void *)ID_MT7621, },
+       { .compatible = "mediatek,mt7530", .data = (void *)ID_MT7530, },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mt7530_of_match);
+
 static int
 mt7530_probe(struct mdio_device *mdiodev)
 {
@@ -1356,13 +1372,21 @@ mt7530_probe(struct mdio_device *mdiodev)
                }
        }
 
-       priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core");
-       if (IS_ERR(priv->core_pwr))
-               return PTR_ERR(priv->core_pwr);
+       /* Get the hardware identifier from the devicetree node.
+        * We will need it for some of the clock and regulator setup.
+        */
+       priv->id = (unsigned int)(unsigned long)
+               of_device_get_match_data(&mdiodev->dev);
 
-       priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io");
-       if (IS_ERR(priv->io_pwr))
-               return PTR_ERR(priv->io_pwr);
+       if (priv->id == ID_MT7530) {
+               priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core");
+               if (IS_ERR(priv->core_pwr))
+                       return PTR_ERR(priv->core_pwr);
+
+               priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io");
+               if (IS_ERR(priv->io_pwr))
+                       return PTR_ERR(priv->io_pwr);
+       }
 
        /* Not MCM that indicates switch works as the remote standalone
         * integrated circuit so the GPIO pin would be used to complete
@@ -1408,12 +1432,6 @@ mt7530_remove(struct mdio_device *mdiodev)
        mutex_destroy(&priv->reg_mutex);
 }
 
-static const struct of_device_id mt7530_of_match[] = {
-       { .compatible = "mediatek,mt7530" },
-       { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, mt7530_of_match);
-
 static struct mdio_driver mt7530_mdio_driver = {
        .probe  = mt7530_probe,
        .remove = mt7530_remove,
index d9b407a..a95ed95 100644 (file)
 #define MT7530_NUM_FDB_RECORDS         2048
 #define MT7530_ALL_MEMBERS             0xff
 
+enum {
+       ID_MT7530 = 0,
+       ID_MT7621 = 1,
+};
+
 #define        NUM_TRGMII_CTRL                 5
 
 #define TRGMII_BASE(x)                 (0x10000 + (x))
@@ -36,6 +41,9 @@
 #define  UNM_FFP(x)                    (((x) & 0xff) << 16)
 #define  UNU_FFP(x)                    (((x) & 0xff) << 8)
 #define  UNU_FFP_MASK                  UNU_FFP(~0)
+#define  CPU_EN                                BIT(7)
+#define  CPU_PORT(x)                   ((x) << 4)
+#define  CPU_MASK                      (0xf << 4)
 
 /* Registers for address table access */
 #define MT7530_ATA1                    0x74
@@ -430,6 +438,7 @@ struct mt7530_priv {
        struct regulator        *core_pwr;
        struct regulator        *io_pwr;
        struct gpio_desc        *reset;
+       unsigned int            id;
        bool                    mcm;
 
        struct mt7530_port      ports[MT7530_NUM_PORTS];
index 8dca2c9..32e7af5 100644 (file)
@@ -261,6 +261,7 @@ static irqreturn_t mv88e6xxx_g1_irq_thread_work(struct mv88e6xxx_chip *chip)
        unsigned int sub_irq;
        unsigned int n;
        u16 reg;
+       u16 ctl1;
        int err;
 
        mutex_lock(&chip->reg_lock);
@@ -270,13 +271,28 @@ static irqreturn_t mv88e6xxx_g1_irq_thread_work(struct mv88e6xxx_chip *chip)
        if (err)
                goto out;
 
-       for (n = 0; n < chip->g1_irq.nirqs; ++n) {
-               if (reg & (1 << n)) {
-                       sub_irq = irq_find_mapping(chip->g1_irq.domain, n);
-                       handle_nested_irq(sub_irq);
-                       ++nhandled;
+       do {
+               for (n = 0; n < chip->g1_irq.nirqs; ++n) {
+                       if (reg & (1 << n)) {
+                               sub_irq = irq_find_mapping(chip->g1_irq.domain,
+                                                          n);
+                               handle_nested_irq(sub_irq);
+                               ++nhandled;
+                       }
                }
-       }
+
+               mutex_lock(&chip->reg_lock);
+               err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &ctl1);
+               if (err)
+                       goto unlock;
+               err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &reg);
+unlock:
+               mutex_unlock(&chip->reg_lock);
+               if (err)
+                       goto out;
+               ctl1 &= GENMASK(chip->g1_irq.nirqs, 0);
+       } while (reg & ctl1);
+
 out:
        return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
 }
@@ -647,8 +663,10 @@ static void mv88e6390_phylink_validate(struct mv88e6xxx_chip *chip, int port,
                                       unsigned long *mask,
                                       struct phylink_link_state *state)
 {
-       if (port >= 9)
+       if (port >= 9) {
                phylink_set(mask, 2500baseX_Full);
+               phylink_set(mask, 2500baseT_Full);
+       }
 
        /* No ethtool bits for 200Mbps */
        phylink_set(mask, 1000baseT_Full);
@@ -4764,6 +4782,21 @@ static const void *pdata_device_get_match_data(struct device *dev)
        return NULL;
 }
 
+/* There is no suspend to RAM support at DSA level yet, the switch configuration
+ * would be lost after a power cycle so prevent it to be suspended.
+ */
+static int __maybe_unused mv88e6xxx_suspend(struct device *dev)
+{
+       return -EOPNOTSUPP;
+}
+
+static int __maybe_unused mv88e6xxx_resume(struct device *dev)
+{
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mv88e6xxx_pm_ops, mv88e6xxx_suspend, mv88e6xxx_resume);
+
 static int mv88e6xxx_probe(struct mdio_device *mdiodev)
 {
        struct dsa_mv88e6xxx_pdata *pdata = mdiodev->dev.platform_data;
@@ -4948,6 +4981,7 @@ static struct mdio_driver mv88e6xxx_driver = {
        .mdiodrv.driver = {
                .name = "mv88e6085",
                .of_match_table = mv88e6xxx_of_match,
+               .pm = &mv88e6xxx_pm_ops,
        },
 };
 
index 5200e4b..ea24384 100644 (file)
@@ -314,6 +314,7 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
 {
        struct mv88e6xxx_chip *chip = dev_id;
        struct mv88e6xxx_atu_entry entry;
+       int spid;
        int err;
        u16 val;
 
@@ -336,6 +337,8 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
        if (err)
                goto out;
 
+       spid = entry.state;
+
        if (val & MV88E6XXX_G1_ATU_OP_AGE_OUT_VIOLATION) {
                dev_err_ratelimited(chip->dev,
                                    "ATU age out violation for %pM\n",
@@ -344,23 +347,23 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
 
        if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION) {
                dev_err_ratelimited(chip->dev,
-                                   "ATU member violation for %pM portvec %x\n",
-                                   entry.mac, entry.portvec);
-               chip->ports[entry.portvec].atu_member_violation++;
+                                   "ATU member violation for %pM portvec %x spid %d\n",
+                                   entry.mac, entry.portvec, spid);
+               chip->ports[spid].atu_member_violation++;
        }
 
        if (val & MV88E6XXX_G1_ATU_OP_MISS_VIOLATION) {
                dev_err_ratelimited(chip->dev,
-                                   "ATU miss violation for %pM portvec %x\n",
-                                   entry.mac, entry.portvec);
-               chip->ports[entry.portvec].atu_miss_violation++;
+                                   "ATU miss violation for %pM portvec %x spid %d\n",
+                                   entry.mac, entry.portvec, spid);
+               chip->ports[spid].atu_miss_violation++;
        }
 
        if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION) {
                dev_err_ratelimited(chip->dev,
-                                   "ATU full violation for %pM portvec %x\n",
-                                   entry.mac, entry.portvec);
-               chip->ports[entry.portvec].atu_full_violation++;
+                                   "ATU full violation for %pM portvec %x spid %d\n",
+                                   entry.mac, entry.portvec, spid);
+               chip->ports[spid].atu_full_violation++;
        }
        mutex_unlock(&chip->reg_lock);
 
index 2caa8c8..1bfc5ff 100644 (file)
@@ -664,7 +664,7 @@ int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
        if (port < 9)
                return 0;
 
-       return mv88e6390_serdes_irq_setup(chip, port);
+       return mv88e6390x_serdes_irq_setup(chip, port);
 }
 
 void mv88e6390x_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
index b648e3f..808abb6 100644 (file)
@@ -1177,7 +1177,7 @@ static irqreturn_t corkscrew_interrupt(int irq, void *dev_id)
                                if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry]))
                                        break;  /* It still hasn't been processed. */
                                if (lp->tx_skbuff[entry]) {
-                                       dev_kfree_skb_irq(lp->tx_skbuff[entry]);
+                                       dev_consume_skb_irq(lp->tx_skbuff[entry]);
                                        lp->tx_skbuff[entry] = NULL;
                                }
                                dirty_tx++;
@@ -1192,7 +1192,7 @@ static irqreturn_t corkscrew_interrupt(int irq, void *dev_id)
 #ifdef VORTEX_BUS_MASTER
                if (status & DMADone) {
                        outw(0x1000, ioaddr + Wn7_MasterStatus);        /* Ack the event. */
-                       dev_kfree_skb_irq(lp->tx_skb);  /* Release the transferred buffer */
+                       dev_consume_skb_irq(lp->tx_skb);        /* Release the transferred buffer */
                        netif_wake_queue(dev);
                }
 #endif
index 40f421d..1470514 100644 (file)
@@ -2307,7 +2307,7 @@ _vortex_interrupt(int irq, struct net_device *dev)
                                dma_unmap_single(vp->gendev, vp->tx_skb_dma, (vp->tx_skb->len + 3) & ~3, DMA_TO_DEVICE);
                                pkts_compl++;
                                bytes_compl += vp->tx_skb->len;
-                               dev_kfree_skb_irq(vp->tx_skb); /* Release the transferred buffer */
+                               dev_consume_skb_irq(vp->tx_skb); /* Release the transferred buffer */
                                if (ioread16(ioaddr + TxFree) > 1536) {
                                        /*
                                         * AKPM: FIXME: I don't think we need this.  If the queue was stopped due to
@@ -2449,7 +2449,7 @@ _boomerang_interrupt(int irq, struct net_device *dev)
 #endif
                                        pkts_compl++;
                                        bytes_compl += skb->len;
-                                       dev_kfree_skb_irq(skb);
+                                       dev_consume_skb_irq(skb);
                                        vp->tx_skbuff[entry] = NULL;
                                } else {
                                        pr_debug("boomerang_interrupt: no skb!\n");
index 097467f..816540e 100644 (file)
@@ -1390,7 +1390,7 @@ static irqreturn_t intr_handler(int irq, void *dev_instance)
                                        }
                                }
 
-                               dev_kfree_skb_irq(skb);
+                               dev_consume_skb_irq(skb);
                        }
                        np->tx_done_q[np->tx_done].status = 0;
                        np->tx_done = (np->tx_done + 1) % DONE_Q_SIZE;
index 4f11f98..1827ef1 100644 (file)
@@ -2059,7 +2059,7 @@ static inline void ace_tx_int(struct net_device *dev,
                if (skb) {
                        dev->stats.tx_packets++;
                        dev->stats.tx_bytes += skb->len;
-                       dev_kfree_skb_irq(skb);
+                       dev_consume_skb_irq(skb);
                        info->skb = NULL;
                }
 
index 0fb986b..0ae723f 100644 (file)
@@ -145,7 +145,8 @@ u32 msgdma_tx_completions(struct altera_tse_private *priv)
                        & 0xffff;
 
        if (inuse) { /* Tx FIFO is not empty */
-               ready = priv->tx_prod - priv->tx_cons - inuse - 1;
+               ready = max_t(int,
+                             priv->tx_prod - priv->tx_cons - inuse - 1, 0);
        } else {
                /* Check for buffered last packet */
                status = csrrd32(priv->tx_dma_csr, msgdma_csroffs(status));
index a70bb1b..a6eacf2 100644 (file)
@@ -2663,11 +2663,6 @@ static int ena_restore_device(struct ena_adapter *adapter)
                goto err_device_destroy;
        }
 
-       clear_bit(ENA_FLAG_ONGOING_RESET, &adapter->flags);
-       /* Make sure we don't have a race with AENQ Links state handler */
-       if (test_bit(ENA_FLAG_LINK_UP, &adapter->flags))
-               netif_carrier_on(adapter->netdev);
-
        rc = ena_enable_msix_and_set_admin_interrupts(adapter,
                                                      adapter->num_queues);
        if (rc) {
@@ -2684,6 +2679,11 @@ static int ena_restore_device(struct ena_adapter *adapter)
        }
 
        set_bit(ENA_FLAG_DEVICE_RUNNING, &adapter->flags);
+
+       clear_bit(ENA_FLAG_ONGOING_RESET, &adapter->flags);
+       if (test_bit(ENA_FLAG_LINK_UP, &adapter->flags))
+               netif_carrier_on(adapter->netdev);
+
        mod_timer(&adapter->timer_service, round_jiffies(jiffies + HZ));
        dev_err(&pdev->dev,
                "Device reset completed successfully, Driver info: %s\n",
index dc8b617..6387007 100644 (file)
@@ -45,7 +45,7 @@
 
 #define DRV_MODULE_VER_MAJOR   2
 #define DRV_MODULE_VER_MINOR   0
-#define DRV_MODULE_VER_SUBMINOR 2
+#define DRV_MODULE_VER_SUBMINOR 3
 
 #define DRV_MODULE_NAME                "ena"
 #ifndef DRV_MODULE_VERSION
index b963292..145fe71 100644 (file)
@@ -666,7 +666,7 @@ static int amd8111e_tx(struct net_device *dev)
                        pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[tx_index],
                                        lp->tx_skbuff[tx_index]->len,
                                        PCI_DMA_TODEVICE);
-                       dev_kfree_skb_irq (lp->tx_skbuff[tx_index]);
+                       dev_consume_skb_irq(lp->tx_skbuff[tx_index]);
                        lp->tx_skbuff[tx_index] = NULL;
                        lp->tx_dma_addr[tx_index] = 0;
                }
index e833d1b..e5073ae 100644 (file)
@@ -1167,7 +1167,7 @@ static int au1000_probe(struct platform_device *pdev)
        /* Allocate the data buffers
         * Snooping works fine with eth on all au1xxx
         */
-       aup->vaddr = (u32)dma_alloc_attrs(NULL, MAX_BUF_SIZE *
+       aup->vaddr = (u32)dma_alloc_attrs(&pdev->dev, MAX_BUF_SIZE *
                                          (NUM_TX_BUFFS + NUM_RX_BUFFS),
                                          &aup->dma_addr, 0,
                                          DMA_ATTR_NON_CONSISTENT);
@@ -1349,7 +1349,7 @@ err_remap3:
 err_remap2:
        iounmap(aup->mac);
 err_remap1:
-       dma_free_attrs(NULL, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
+       dma_free_attrs(&pdev->dev, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
                        (void *)aup->vaddr, aup->dma_addr,
                        DMA_ATTR_NON_CONSISTENT);
 err_vaddr:
@@ -1383,7 +1383,7 @@ static int au1000_remove(struct platform_device *pdev)
                if (aup->tx_db_inuse[i])
                        au1000_ReleaseDB(aup, aup->tx_db_inuse[i]);
 
-       dma_free_attrs(NULL, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
+       dma_free_attrs(&pdev->dev, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
                        (void *)aup->vaddr, aup->dma_addr,
                        DMA_ATTR_NON_CONSISTENT);
 
index b56d84c..f90b454 100644 (file)
@@ -1084,7 +1084,7 @@ static irqreturn_t lance_interrupt(int irq, void *dev_id)
                                /* We must free the original skb if it's not a data-only copy
                                   in the bounce buffer. */
                                if (lp->tx_skbuff[entry]) {
-                                       dev_kfree_skb_irq(lp->tx_skbuff[entry]);
+                                       dev_consume_skb_irq(lp->tx_skbuff[entry]);
                                        lp->tx_skbuff[entry] = NULL;
                                }
                                dirty_tx++;
index 8931ce6..87ff5d6 100644 (file)
@@ -1028,7 +1028,7 @@ static void ni65_xmit_intr(struct net_device *dev,int csr0)
 
 #ifdef XMT_VIA_SKB
                if(p->tmd_skb[p->tmdlast]) {
-                        dev_kfree_skb_irq(p->tmd_skb[p->tmdlast]);
+                        dev_consume_skb_irq(p->tmd_skb[p->tmdlast]);
                         p->tmd_skb[p->tmdlast] = NULL;
                }
 #endif
index 6a8e256..4d3855c 100644 (file)
@@ -777,7 +777,7 @@ static irqreturn_t bmac_txdma_intr(int irq, void *dev_id)
 
                if (bp->tx_bufs[bp->tx_empty]) {
                        ++dev->stats.tx_packets;
-                       dev_kfree_skb_irq(bp->tx_bufs[bp->tx_empty]);
+                       dev_consume_skb_irq(bp->tx_bufs[bp->tx_empty]);
                }
                bp->tx_bufs[bp->tx_empty] = NULL;
                bp->tx_fullup = 0;
index 68b9ee4..4d9819d 100644 (file)
@@ -764,7 +764,7 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id)
            dev->stats.tx_bytes += mp->tx_bufs[i]->len;
            ++dev->stats.tx_packets;
        }
-       dev_kfree_skb_irq(mp->tx_bufs[i]);
+       dev_consume_skb_irq(mp->tx_bufs[i]);
        --mp->tx_active;
        if (++i >= N_TX_RING)
            i = 0;
index 4406325..ff3d685 100644 (file)
@@ -148,7 +148,7 @@ static void arc_emac_tx_clean(struct net_device *ndev)
                                 dma_unmap_len(tx_buff, len), DMA_TO_DEVICE);
 
                /* return the sk_buff to system */
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
 
                txbd->data = 0;
                txbd->info = 0;
index 3a3b35b..0f1eb19 100644 (file)
@@ -1833,10 +1833,10 @@ rrs_checked:
                atl1c_clean_rrd(rrd_ring, rrs, rfd_num);
                if (rrs->word3 & (RRS_RX_ERR_SUM | RRS_802_3_LEN_ERR)) {
                        atl1c_clean_rfd(rfd_ring, rrs, rfd_num);
-                               if (netif_msg_rx_err(adapter))
-                                       dev_warn(&pdev->dev,
-                                               "wrong packet! rrs word3 is %x\n",
-                                               rrs->word3);
+                       if (netif_msg_rx_err(adapter))
+                               dev_warn(&pdev->dev,
+                                        "wrong packet! rrs word3 is %x\n",
+                                        rrs->word3);
                        continue;
                }
 
index 3164aad..9dfe6a9 100644 (file)
@@ -1259,7 +1259,7 @@ static bool atl1e_clean_tx_irq(struct atl1e_adapter *adapter)
                }
 
                if (tx_buffer->skb) {
-                       dev_kfree_skb_irq(tx_buffer->skb);
+                       dev_consume_skb_irq(tx_buffer->skb);
                        tx_buffer->skb = NULL;
                }
 
index 63edc57..9e07b46 100644 (file)
@@ -2088,7 +2088,7 @@ static int atl1_intr_tx(struct atl1_adapter *adapter)
                }
 
                if (buffer_info->skb) {
-                       dev_kfree_skb_irq(buffer_info->skb);
+                       dev_consume_skb_irq(buffer_info->skb);
                        buffer_info->skb = NULL;
                }
 
index c44e952..a7337a6 100644 (file)
@@ -2946,7 +2946,7 @@ static int atl2_validate_option(int *value, struct atl2_option *opt)
                        if (*value == ent->i) {
                                if (ent->str[0] != '\0')
                                        printk(KERN_INFO "%s\n", ent->str);
-                       return 0;
+                               return 0;
                        }
                }
                break;
index f448089..97ab0dd 100644 (file)
@@ -638,7 +638,7 @@ static void b44_tx(struct b44 *bp)
                bytes_compl += skb->len;
                pkts_compl++;
 
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
        }
 
        netdev_completed_queue(bp->dev, pkts_compl, bytes_compl);
@@ -1012,7 +1012,7 @@ static netdev_tx_t b44_start_xmit(struct sk_buff *skb, struct net_device *dev)
                }
 
                skb_copy_from_linear_data(skb, skb_put(bounce_skb, len), len);
-               dev_kfree_skb_any(skb);
+               dev_consume_skb_any(skb);
                skb = bounce_skb;
        }
 
index f9521d0..28c9b0b 100644 (file)
@@ -520,7 +520,6 @@ static void bcm_sysport_get_wol(struct net_device *dev,
                                struct ethtool_wolinfo *wol)
 {
        struct bcm_sysport_priv *priv = netdev_priv(dev);
-       u32 reg;
 
        wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
        wol->wolopts = priv->wolopts;
@@ -528,11 +527,7 @@ static void bcm_sysport_get_wol(struct net_device *dev,
        if (!(priv->wolopts & WAKE_MAGICSECURE))
                return;
 
-       /* Return the programmed SecureOn password */
-       reg = umac_readl(priv, UMAC_PSW_MS);
-       put_unaligned_be16(reg, &wol->sopass[0]);
-       reg = umac_readl(priv, UMAC_PSW_LS);
-       put_unaligned_be32(reg, &wol->sopass[2]);
+       memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));
 }
 
 static int bcm_sysport_set_wol(struct net_device *dev,
@@ -548,13 +543,8 @@ static int bcm_sysport_set_wol(struct net_device *dev,
        if (wol->wolopts & ~supported)
                return -EINVAL;
 
-       /* Program the SecureOn password */
-       if (wol->wolopts & WAKE_MAGICSECURE) {
-               umac_writel(priv, get_unaligned_be16(&wol->sopass[0]),
-                           UMAC_PSW_MS);
-               umac_writel(priv, get_unaligned_be32(&wol->sopass[2]),
-                           UMAC_PSW_LS);
-       }
+       if (wol->wolopts & WAKE_MAGICSECURE)
+               memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));
 
        /* Flag the device and relevant IRQ as wakeup capable */
        if (wol->wolopts) {
@@ -2649,13 +2639,18 @@ static int bcm_sysport_suspend_to_wol(struct bcm_sysport_priv *priv)
        unsigned int index, i = 0;
        u32 reg;
 
-       /* Password has already been programmed */
        reg = umac_readl(priv, UMAC_MPD_CTRL);
        if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE))
                reg |= MPD_EN;
        reg &= ~PSW_EN;
-       if (priv->wolopts & WAKE_MAGICSECURE)
+       if (priv->wolopts & WAKE_MAGICSECURE) {
+               /* Program the SecureOn password */
+               umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),
+                           UMAC_PSW_MS);
+               umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),
+                           UMAC_PSW_LS);
                reg |= PSW_EN;
+       }
        umac_writel(priv, reg, UMAC_MPD_CTRL);
 
        if (priv->wolopts & WAKE_FILTER) {
index 0887e63..0b192fe 100644 (file)
@@ -12,6 +12,7 @@
 #define __BCM_SYSPORT_H
 
 #include <linux/bitmap.h>
+#include <linux/ethtool.h>
 #include <linux/if_vlan.h>
 #include <linux/net_dim.h>
 
@@ -778,6 +779,7 @@ struct bcm_sysport_priv {
        unsigned int            crc_fwd:1;
        u16                     rev;
        u32                     wolopts;
+       u8                      sopass[SOPASS_MAX];
        unsigned int            wol_irq_disabled:1;
 
        /* MIB related fields */
index 2d3a44c..4632dd5 100644 (file)
@@ -1446,7 +1446,7 @@ int bgmac_phy_connect_direct(struct bgmac *bgmac)
        struct phy_device *phy_dev;
        int err;
 
-       phy_dev = fixed_phy_register(PHY_POLL, &fphy_status, -1, NULL);
+       phy_dev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
        if (!phy_dev || IS_ERR(phy_dev)) {
                dev_err(bgmac->dev, "Failed to register fixed PHY device\n");
                return -ENODEV;
index 8e0a317..a9bdc21 100644 (file)
@@ -1654,13 +1654,9 @@ static int bnx2x_vf_mbx_macvlan_list(struct bnx2x *bp,
 {
        int i, j;
        struct bnx2x_vf_mac_vlan_filters *fl = NULL;
-       size_t fsz;
 
-       fsz = tlv->n_mac_vlan_filters *
-             sizeof(struct bnx2x_vf_mac_vlan_filter) +
-             sizeof(struct bnx2x_vf_mac_vlan_filters);
-
-       fl = kzalloc(fsz, GFP_KERNEL);
+       fl = kzalloc(struct_size(fl, filters, tlv->n_mac_vlan_filters),
+                    GFP_KERNEL);
        if (!fl)
                return -ENOMEM;
 
index 6a51287..92d7345 100644 (file)
@@ -4973,12 +4973,18 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp)
                struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
                struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
                u32 map_idx = ring->map_idx;
+               unsigned int vector;
 
+               vector = bp->irq_tbl[map_idx].vector;
+               disable_irq_nosync(vector);
                rc = hwrm_ring_alloc_send_msg(bp, ring, type, map_idx);
-               if (rc)
+               if (rc) {
+                       enable_irq(vector);
                        goto err_out;
+               }
                bnxt_set_db(bp, &cpr->cp_db, type, map_idx, ring->fw_ring_id);
                bnxt_db_nq(bp, &cpr->cp_db, cpr->cp_raw_cons);
+               enable_irq(vector);
                bp->grp_info[i].cp_fw_ring_id = ring->fw_ring_id;
 
                if (!i) {
@@ -9981,8 +9987,11 @@ static int bnxt_get_phys_port_name(struct net_device *dev, char *buf,
        return 0;
 }
 
-int bnxt_port_attr_get(struct bnxt *bp, struct switchdev_attr *attr)
+int bnxt_get_port_parent_id(struct net_device *dev,
+                           struct netdev_phys_item_id *ppid)
 {
+       struct bnxt *bp = netdev_priv(dev);
+
        if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
                return -EOPNOTSUPP;
 
@@ -9990,27 +9999,12 @@ int bnxt_port_attr_get(struct bnxt *bp, struct switchdev_attr *attr)
        if (!BNXT_PF(bp))
                return -EOPNOTSUPP;
 
-       switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = sizeof(bp->switch_id);
-               memcpy(attr->u.ppid.id, bp->switch_id, attr->u.ppid.id_len);
-               break;
-       default:
-               return -EOPNOTSUPP;
-       }
-       return 0;
-}
+       ppid->id_len = sizeof(bp->switch_id);
+       memcpy(ppid->id, bp->switch_id, ppid->id_len);
 
-static int bnxt_swdev_port_attr_get(struct net_device *dev,
-                                   struct switchdev_attr *attr)
-{
-       return bnxt_port_attr_get(netdev_priv(dev), attr);
+       return 0;
 }
 
-static const struct switchdev_ops bnxt_switchdev_ops = {
-       .switchdev_port_attr_get        = bnxt_swdev_port_attr_get
-};
-
 static const struct net_device_ops bnxt_netdev_ops = {
        .ndo_open               = bnxt_open,
        .ndo_start_xmit         = bnxt_start_xmit,
@@ -10042,6 +10036,7 @@ static const struct net_device_ops bnxt_netdev_ops = {
        .ndo_bpf                = bnxt_xdp,
        .ndo_bridge_getlink     = bnxt_bridge_getlink,
        .ndo_bridge_setlink     = bnxt_bridge_setlink,
+       .ndo_get_port_parent_id = bnxt_get_port_parent_id,
        .ndo_get_phys_port_name = bnxt_get_phys_port_name
 };
 
@@ -10400,7 +10395,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        dev->netdev_ops = &bnxt_netdev_ops;
        dev->watchdog_timeo = BNXT_TX_TIMEOUT;
        dev->ethtool_ops = &bnxt_ethtool_ops;
-       SWITCHDEV_SET_OPS(dev, &bnxt_switchdev_ops);
        pci_set_drvdata(pdev, dev);
 
        rc = bnxt_alloc_hwrm_resources(bp);
index a451796..17554d4 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/rhashtable.h>
 #include <net/devlink.h>
 #include <net/dst_metadata.h>
-#include <net/switchdev.h>
 #include <net/xdp.h>
 #include <linux/net_dim.h>
 
@@ -1609,6 +1608,7 @@ struct bnxt {
 
        /* devlink interface and vf-rep structs */
        struct devlink          *dl;
+       struct devlink_port     dl_port;
        enum devlink_eswitch_mode eswitch_mode;
        struct bnxt_vf_rep      **vf_reps; /* array of vf-rep ptrs */
        u16                     *cfa_code_map; /* cfa_code -> vf_idx map */
@@ -1794,7 +1794,8 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
 int bnxt_setup_mq_tc(struct net_device *dev, u8 tc);
 int bnxt_get_max_rings(struct bnxt *, int *, int *, bool);
 int bnxt_restore_pf_fw_resources(struct bnxt *bp);
-int bnxt_port_attr_get(struct bnxt *bp, struct switchdev_attr *attr);
+int bnxt_get_port_parent_id(struct net_device *dev,
+                           struct netdev_phys_item_id *ppid);
 void bnxt_dim_work(struct work_struct *work);
 int bnxt_hwrm_set_ring_coal(struct bnxt *bp, struct bnxt_napi *bnapi);
 
index 7f56032..e1feb97 100644 (file)
@@ -188,6 +188,9 @@ static const struct devlink_param bnxt_dl_params[] = {
                             NULL),
 };
 
+static const struct devlink_param bnxt_dl_port_params[] = {
+};
+
 int bnxt_dl_register(struct bnxt *bp)
 {
        struct devlink *dl;
@@ -225,8 +228,29 @@ int bnxt_dl_register(struct bnxt *bp)
                goto err_dl_unreg;
        }
 
+       rc = devlink_port_register(dl, &bp->dl_port, bp->pf.port_id);
+       if (rc) {
+               netdev_err(bp->dev, "devlink_port_register failed");
+               goto err_dl_param_unreg;
+       }
+       devlink_port_type_eth_set(&bp->dl_port, bp->dev);
+
+       rc = devlink_port_params_register(&bp->dl_port, bnxt_dl_port_params,
+                                         ARRAY_SIZE(bnxt_dl_port_params));
+       if (rc) {
+               netdev_err(bp->dev, "devlink_port_params_register failed");
+               goto err_dl_port_unreg;
+       }
+
+       devlink_params_publish(dl);
+
        return 0;
 
+err_dl_port_unreg:
+       devlink_port_unregister(&bp->dl_port);
+err_dl_param_unreg:
+       devlink_params_unregister(dl, bnxt_dl_params,
+                                 ARRAY_SIZE(bnxt_dl_params));
 err_dl_unreg:
        devlink_unregister(dl);
 err_dl_free:
@@ -242,6 +266,9 @@ void bnxt_dl_unregister(struct bnxt *bp)
        if (!dl)
                return;
 
+       devlink_port_params_unregister(&bp->dl_port, bnxt_dl_port_params,
+                                      ARRAY_SIZE(bnxt_dl_port_params));
+       devlink_port_unregister(&bp->dl_port);
        devlink_params_unregister(dl, bnxt_dl_params,
                                  ARRAY_SIZE(bnxt_dl_params));
        devlink_unregister(dl);
index c683b5e..61a3457 100644 (file)
@@ -45,7 +45,7 @@ static u16 bnxt_flow_get_dst_fid(struct bnxt *pf_bp, struct net_device *dev)
        struct bnxt *bp;
 
        /* check if dev belongs to the same switch */
-       if (!switchdev_port_same_parent_id(pf_bp->dev, dev)) {
+       if (!netdev_port_same_parent_id(pf_bp->dev, dev)) {
                netdev_info(pf_bp->dev, "dev(ifindex=%d) not on same switch",
                            dev->ifindex);
                return BNXT_FID_INVALID;
@@ -61,9 +61,9 @@ static u16 bnxt_flow_get_dst_fid(struct bnxt *pf_bp, struct net_device *dev)
 
 static int bnxt_tc_parse_redir(struct bnxt *bp,
                               struct bnxt_tc_actions *actions,
-                              const struct tc_action *tc_act)
+                              const struct flow_action_entry *act)
 {
-       struct net_device *dev = tcf_mirred_dev(tc_act);
+       struct net_device *dev = act->dev;
 
        if (!dev) {
                netdev_info(bp->dev, "no dev in mirred action");
@@ -77,16 +77,16 @@ static int bnxt_tc_parse_redir(struct bnxt *bp,
 
 static int bnxt_tc_parse_vlan(struct bnxt *bp,
                              struct bnxt_tc_actions *actions,
-                             const struct tc_action *tc_act)
+                             const struct flow_action_entry *act)
 {
-       switch (tcf_vlan_action(tc_act)) {
-       case TCA_VLAN_ACT_POP:
+       switch (act->id) {
+       case FLOW_ACTION_VLAN_POP:
                actions->flags |= BNXT_TC_ACTION_FLAG_POP_VLAN;
                break;
-       case TCA_VLAN_ACT_PUSH:
+       case FLOW_ACTION_VLAN_PUSH:
                actions->flags |= BNXT_TC_ACTION_FLAG_PUSH_VLAN;
-               actions->push_vlan_tci = htons(tcf_vlan_push_vid(tc_act));
-               actions->push_vlan_tpid = tcf_vlan_push_proto(tc_act);
+               actions->push_vlan_tci = htons(act->vlan.vid);
+               actions->push_vlan_tpid = act->vlan.proto;
                break;
        default:
                return -EOPNOTSUPP;
@@ -96,10 +96,10 @@ static int bnxt_tc_parse_vlan(struct bnxt *bp,
 
 static int bnxt_tc_parse_tunnel_set(struct bnxt *bp,
                                    struct bnxt_tc_actions *actions,
-                                   const struct tc_action *tc_act)
+                                   const struct flow_action_entry *act)
 {
-       struct ip_tunnel_info *tun_info = tcf_tunnel_info(tc_act);
-       struct ip_tunnel_key *tun_key = &tun_info->key;
+       const struct ip_tunnel_info *tun_info = act->tunnel;
+       const struct ip_tunnel_key *tun_key = &tun_info->key;
 
        if (ip_tunnel_info_af(tun_info) != AF_INET) {
                netdev_info(bp->dev, "only IPv4 tunnel-encap is supported");
@@ -113,51 +113,43 @@ static int bnxt_tc_parse_tunnel_set(struct bnxt *bp,
 
 static int bnxt_tc_parse_actions(struct bnxt *bp,
                                 struct bnxt_tc_actions *actions,
-                                struct tcf_exts *tc_exts)
+                                struct flow_action *flow_action)
 {
-       const struct tc_action *tc_act;
+       struct flow_action_entry *act;
        int i, rc;
 
-       if (!tcf_exts_has_actions(tc_exts)) {
+       if (!flow_action_has_entries(flow_action)) {
                netdev_info(bp->dev, "no actions");
                return -EINVAL;
        }
 
-       tcf_exts_for_each_action(i, tc_act, tc_exts) {
-               /* Drop action */
-               if (is_tcf_gact_shot(tc_act)) {
+       flow_action_for_each(i, act, flow_action) {
+               switch (act->id) {
+               case FLOW_ACTION_DROP:
                        actions->flags |= BNXT_TC_ACTION_FLAG_DROP;
                        return 0; /* don't bother with other actions */
-               }
-
-               /* Redirect action */
-               if (is_tcf_mirred_egress_redirect(tc_act)) {
-                       rc = bnxt_tc_parse_redir(bp, actions, tc_act);
+               case FLOW_ACTION_REDIRECT:
+                       rc = bnxt_tc_parse_redir(bp, actions, act);
                        if (rc)
                                return rc;
-                       continue;
-               }
-
-               /* Push/pop VLAN */
-               if (is_tcf_vlan(tc_act)) {
-                       rc = bnxt_tc_parse_vlan(bp, actions, tc_act);
+                       break;
+               case FLOW_ACTION_VLAN_POP:
+               case FLOW_ACTION_VLAN_PUSH:
+               case FLOW_ACTION_VLAN_MANGLE:
+                       rc = bnxt_tc_parse_vlan(bp, actions, act);
                        if (rc)
                                return rc;
-                       continue;
-               }
-
-               /* Tunnel encap */
-               if (is_tcf_tunnel_set(tc_act)) {
-                       rc = bnxt_tc_parse_tunnel_set(bp, actions, tc_act);
+                       break;
+               case FLOW_ACTION_TUNNEL_ENCAP:
+                       rc = bnxt_tc_parse_tunnel_set(bp, actions, act);
                        if (rc)
                                return rc;
-                       continue;
-               }
-
-               /* Tunnel decap */
-               if (is_tcf_tunnel_release(tc_act)) {
+                       break;
+               case FLOW_ACTION_TUNNEL_DECAP:
                        actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_DECAP;
-                       continue;
+                       break;
+               default:
+                       break;
                }
        }
 
@@ -177,18 +169,12 @@ static int bnxt_tc_parse_actions(struct bnxt *bp,
        return 0;
 }
 
-#define GET_KEY(flow_cmd, key_type)                                    \
-               skb_flow_dissector_target((flow_cmd)->dissector, key_type,\
-                                         (flow_cmd)->key)
-#define GET_MASK(flow_cmd, key_type)                                   \
-               skb_flow_dissector_target((flow_cmd)->dissector, key_type,\
-                                         (flow_cmd)->mask)
-
 static int bnxt_tc_parse_flow(struct bnxt *bp,
                              struct tc_cls_flower_offload *tc_flow_cmd,
                              struct bnxt_tc_flow *flow)
 {
-       struct flow_dissector *dissector = tc_flow_cmd->dissector;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(tc_flow_cmd);
+       struct flow_dissector *dissector = rule->match.dissector;
 
        /* KEY_CONTROL and KEY_BASIC are needed for forming a meaningful key */
        if ((dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL)) == 0 ||
@@ -198,143 +184,123 @@ static int bnxt_tc_parse_flow(struct bnxt *bp,
                return -EOPNOTSUPP;
        }
 
-       if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *key =
-                       GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_BASIC);
-               struct flow_dissector_key_basic *mask =
-                       GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_BASIC);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
 
-               flow->l2_key.ether_type = key->n_proto;
-               flow->l2_mask.ether_type = mask->n_proto;
+               flow_rule_match_basic(rule, &match);
+               flow->l2_key.ether_type = match.key->n_proto;
+               flow->l2_mask.ether_type = match.mask->n_proto;
 
-               if (key->n_proto == htons(ETH_P_IP) ||
-                   key->n_proto == htons(ETH_P_IPV6)) {
-                       flow->l4_key.ip_proto = key->ip_proto;
-                       flow->l4_mask.ip_proto = mask->ip_proto;
+               if (match.key->n_proto == htons(ETH_P_IP) ||
+                   match.key->n_proto == htons(ETH_P_IPV6)) {
+                       flow->l4_key.ip_proto = match.key->ip_proto;
+                       flow->l4_mask.ip_proto = match.mask->ip_proto;
                }
        }
 
-       if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
-               struct flow_dissector_key_eth_addrs *key =
-                       GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ETH_ADDRS);
-               struct flow_dissector_key_eth_addrs *mask =
-                       GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ETH_ADDRS);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct flow_match_eth_addrs match;
 
+               flow_rule_match_eth_addrs(rule, &match);
                flow->flags |= BNXT_TC_FLOW_FLAGS_ETH_ADDRS;
-               ether_addr_copy(flow->l2_key.dmac, key->dst);
-               ether_addr_copy(flow->l2_mask.dmac, mask->dst);
-               ether_addr_copy(flow->l2_key.smac, key->src);
-               ether_addr_copy(flow->l2_mask.smac, mask->src);
+               ether_addr_copy(flow->l2_key.dmac, match.key->dst);
+               ether_addr_copy(flow->l2_mask.dmac, match.mask->dst);
+               ether_addr_copy(flow->l2_key.smac, match.key->src);
+               ether_addr_copy(flow->l2_mask.smac, match.mask->src);
        }
 
-       if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_VLAN)) {
-               struct flow_dissector_key_vlan *key =
-                       GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_VLAN);
-               struct flow_dissector_key_vlan *mask =
-                       GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_VLAN);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+               struct flow_match_vlan match;
 
+               flow_rule_match_vlan(rule, &match);
                flow->l2_key.inner_vlan_tci =
-                  cpu_to_be16(VLAN_TCI(key->vlan_id, key->vlan_priority));
+                       cpu_to_be16(VLAN_TCI(match.key->vlan_id,
+                                            match.key->vlan_priority));
                flow->l2_mask.inner_vlan_tci =
-                  cpu_to_be16((VLAN_TCI(mask->vlan_id, mask->vlan_priority)));
+                       cpu_to_be16((VLAN_TCI(match.mask->vlan_id,
+                                             match.mask->vlan_priority)));
                flow->l2_key.inner_vlan_tpid = htons(ETH_P_8021Q);
                flow->l2_mask.inner_vlan_tpid = htons(0xffff);
                flow->l2_key.num_vlans = 1;
        }
 
-       if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
-               struct flow_dissector_key_ipv4_addrs *key =
-                       GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV4_ADDRS);
-               struct flow_dissector_key_ipv4_addrs *mask =
-                       GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV4_ADDRS);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
+               struct flow_match_ipv4_addrs match;
 
+               flow_rule_match_ipv4_addrs(rule, &match);
                flow->flags |= BNXT_TC_FLOW_FLAGS_IPV4_ADDRS;
-               flow->l3_key.ipv4.daddr.s_addr = key->dst;
-               flow->l3_mask.ipv4.daddr.s_addr = mask->dst;
-               flow->l3_key.ipv4.saddr.s_addr = key->src;
-               flow->l3_mask.ipv4.saddr.s_addr = mask->src;
-       } else if (dissector_uses_key(dissector,
-                                     FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
-               struct flow_dissector_key_ipv6_addrs *key =
-                       GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV6_ADDRS);
-               struct flow_dissector_key_ipv6_addrs *mask =
-                       GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV6_ADDRS);
-
+               flow->l3_key.ipv4.daddr.s_addr = match.key->dst;
+               flow->l3_mask.ipv4.daddr.s_addr = match.mask->dst;
+               flow->l3_key.ipv4.saddr.s_addr = match.key->src;
+               flow->l3_mask.ipv4.saddr.s_addr = match.mask->src;
+       } else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
+               struct flow_match_ipv6_addrs match;
+
+               flow_rule_match_ipv6_addrs(rule, &match);
                flow->flags |= BNXT_TC_FLOW_FLAGS_IPV6_ADDRS;
-               flow->l3_key.ipv6.daddr = key->dst;
-               flow->l3_mask.ipv6.daddr = mask->dst;
-               flow->l3_key.ipv6.saddr = key->src;
-               flow->l3_mask.ipv6.saddr = mask->src;
+               flow->l3_key.ipv6.daddr = match.key->dst;
+               flow->l3_mask.ipv6.daddr = match.mask->dst;
+               flow->l3_key.ipv6.saddr = match.key->src;
+               flow->l3_mask.ipv6.saddr = match.mask->src;
        }
 
-       if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_PORTS)) {
-               struct flow_dissector_key_ports *key =
-                       GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_PORTS);
-               struct flow_dissector_key_ports *mask =
-                       GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_PORTS);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+               struct flow_match_ports match;
 
+               flow_rule_match_ports(rule, &match);
                flow->flags |= BNXT_TC_FLOW_FLAGS_PORTS;
-               flow->l4_key.ports.dport = key->dst;
-               flow->l4_mask.ports.dport = mask->dst;
-               flow->l4_key.ports.sport = key->src;
-               flow->l4_mask.ports.sport = mask->src;
+               flow->l4_key.ports.dport = match.key->dst;
+               flow->l4_mask.ports.dport = match.mask->dst;
+               flow->l4_key.ports.sport = match.key->src;
+               flow->l4_mask.ports.sport = match.mask->src;
        }
 
-       if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ICMP)) {
-               struct flow_dissector_key_icmp *key =
-                       GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ICMP);
-               struct flow_dissector_key_icmp *mask =
-                       GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ICMP);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ICMP)) {
+               struct flow_match_icmp match;
 
+               flow_rule_match_icmp(rule, &match);
                flow->flags |= BNXT_TC_FLOW_FLAGS_ICMP;
-               flow->l4_key.icmp.type = key->type;
-               flow->l4_key.icmp.code = key->code;
-               flow->l4_mask.icmp.type = mask->type;
-               flow->l4_mask.icmp.code = mask->code;
+               flow->l4_key.icmp.type = match.key->type;
+               flow->l4_key.icmp.code = match.key->code;
+               flow->l4_mask.icmp.type = match.mask->type;
+               flow->l4_mask.icmp.code = match.mask->code;
        }
 
-       if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) {
-               struct flow_dissector_key_ipv4_addrs *key =
-                       GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS);
-               struct flow_dissector_key_ipv4_addrs *mask =
-                               GET_MASK(tc_flow_cmd,
-                                        FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) {
+               struct flow_match_ipv4_addrs match;
 
+               flow_rule_match_enc_ipv4_addrs(rule, &match);
                flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS;
-               flow->tun_key.u.ipv4.dst = key->dst;
-               flow->tun_mask.u.ipv4.dst = mask->dst;
-               flow->tun_key.u.ipv4.src = key->src;
-               flow->tun_mask.u.ipv4.src = mask->src;
-       } else if (dissector_uses_key(dissector,
+               flow->tun_key.u.ipv4.dst = match.key->dst;
+               flow->tun_mask.u.ipv4.dst = match.mask->dst;
+               flow->tun_key.u.ipv4.src = match.key->src;
+               flow->tun_mask.u.ipv4.src = match.mask->src;
+       } else if (flow_rule_match_key(rule,
                                      FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) {
                return -EOPNOTSUPP;
        }
 
-       if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
-               struct flow_dissector_key_keyid *key =
-                       GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ENC_KEYID);
-               struct flow_dissector_key_keyid *mask =
-                       GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ENC_KEYID);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+               struct flow_match_enc_keyid match;
 
+               flow_rule_match_enc_keyid(rule, &match);
                flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_ID;
-               flow->tun_key.tun_id = key32_to_tunnel_id(key->keyid);
-               flow->tun_mask.tun_id = key32_to_tunnel_id(mask->keyid);
+               flow->tun_key.tun_id = key32_to_tunnel_id(match.key->keyid);
+               flow->tun_mask.tun_id = key32_to_tunnel_id(match.mask->keyid);
        }
 
-       if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ENC_PORTS)) {
-               struct flow_dissector_key_ports *key =
-                       GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ENC_PORTS);
-               struct flow_dissector_key_ports *mask =
-                       GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ENC_PORTS);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS)) {
+               struct flow_match_ports match;
 
+               flow_rule_match_enc_ports(rule, &match);
                flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_PORTS;
-               flow->tun_key.tp_dst = key->dst;
-               flow->tun_mask.tp_dst = mask->dst;
-               flow->tun_key.tp_src = key->src;
-               flow->tun_mask.tp_src = mask->src;
+               flow->tun_key.tp_dst = match.key->dst;
+               flow->tun_mask.tp_dst = match.mask->dst;
+               flow->tun_key.tp_src = match.key->src;
+               flow->tun_mask.tp_src = match.mask->src;
        }
 
-       return bnxt_tc_parse_actions(bp, &flow->actions, tc_flow_cmd->exts);
+       return bnxt_tc_parse_actions(bp, &flow->actions, &rule->action);
 }
 
 static int bnxt_hwrm_cfa_flow_free(struct bnxt *bp,
@@ -1422,8 +1388,8 @@ static int bnxt_tc_get_flow_stats(struct bnxt *bp,
        lastused = flow->lastused;
        spin_unlock(&flow->stats_lock);
 
-       tcf_exts_stats_update(tc_flow_cmd->exts, stats.bytes, stats.packets,
-                             lastused);
+       flow_stats_update(&tc_flow_cmd->stats, stats.bytes, stats.packets,
+                         lastused);
        return 0;
 }
 
index 9a25c05..2bdd2da 100644 (file)
@@ -237,21 +237,17 @@ static void bnxt_vf_rep_get_drvinfo(struct net_device *dev,
        strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
 }
 
-static int bnxt_vf_rep_port_attr_get(struct net_device *dev,
-                                    struct switchdev_attr *attr)
+static int bnxt_vf_rep_get_port_parent_id(struct net_device *dev,
+                                         struct netdev_phys_item_id *ppid)
 {
        struct bnxt_vf_rep *vf_rep = netdev_priv(dev);
 
        /* as only PORT_PARENT_ID is supported currently use common code
         * between PF and VF-rep for now.
         */
-       return bnxt_port_attr_get(vf_rep->bp, attr);
+       return bnxt_get_port_parent_id(vf_rep->bp->dev, ppid);
 }
 
-static const struct switchdev_ops bnxt_vf_rep_switchdev_ops = {
-       .switchdev_port_attr_get        = bnxt_vf_rep_port_attr_get
-};
-
 static const struct ethtool_ops bnxt_vf_rep_ethtool_ops = {
        .get_drvinfo            = bnxt_vf_rep_get_drvinfo
 };
@@ -262,6 +258,7 @@ static const struct net_device_ops bnxt_vf_rep_netdev_ops = {
        .ndo_start_xmit         = bnxt_vf_rep_xmit,
        .ndo_get_stats64        = bnxt_vf_rep_get_stats64,
        .ndo_setup_tc           = bnxt_vf_rep_setup_tc,
+       .ndo_get_port_parent_id = bnxt_vf_rep_get_port_parent_id,
        .ndo_get_phys_port_name = bnxt_vf_rep_get_phys_port_name
 };
 
@@ -392,7 +389,6 @@ static void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep,
 
        dev->netdev_ops = &bnxt_vf_rep_netdev_ops;
        dev->ethtool_ops = &bnxt_vf_rep_ethtool_ops;
-       SWITCHDEV_SET_OPS(dev, &bnxt_vf_rep_switchdev_ops);
        /* Just inherit all the featues of the parent PF as the VF-R
         * uses the RX/TX rings of the parent PF
         */
index aceb9b7..51880d8 100644 (file)
@@ -525,7 +525,7 @@ static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
                        .asym_pause = 0,
                };
 
-               phydev = fixed_phy_register(PHY_POLL, &fphy_status, -1, NULL);
+               phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
                if (!phydev || IS_ERR(phydev)) {
                        dev_err(kdev, "failed to register fixed PHY device\n");
                        return -ENODEV;
index 5db9f41..134ae28 100644 (file)
@@ -1288,7 +1288,7 @@ static void sbdma_tx_process(struct sbmac_softc *sc, struct sbmacdma *d,
                 * for transmits, we just free buffers.
                 */
 
-               dev_kfree_skb_irq(sb);
+               dev_consume_skb_irq(sb);
 
                /*
                 * .. and advance to the next buffer.
index cbc9175..84741d2 100644 (file)
@@ -743,7 +743,7 @@ bfa_iocpf_sm_hwinit(struct bfa_iocpf *iocpf, enum iocpf_event event)
 
        case IOCPF_E_TIMEOUT:
                bfa_nw_ioc_hw_sem_release(ioc);
-                       bfa_ioc_pf_failed(ioc);
+               bfa_ioc_pf_failed(ioc);
                bfa_fsm_set_state(iocpf, bfa_iocpf_sm_initfail_sync);
                break;
 
index 3d45f4c..9bbaad9 100644 (file)
 #define MACB_CAPS_JUMBO                                0x00000020
 #define MACB_CAPS_GEM_HAS_PTP                  0x00000040
 #define MACB_CAPS_BD_RD_PREFETCH               0x00000080
+#define MACB_CAPS_NEEDS_RSTONUBR               0x00000100
 #define MACB_CAPS_FIFO_MODE                    0x10000000
 #define MACB_CAPS_GIGABIT_MODE_AVAILABLE       0x20000000
 #define MACB_CAPS_SG_DISABLED                  0x40000000
@@ -1214,6 +1215,8 @@ struct macb {
 
        int     rx_bd_rd_prefetch;
        int     tx_bd_rd_prefetch;
+
+       u32     rx_intr_mask;
 };
 
 #ifdef CONFIG_MACB_USE_HWSTAMP
index 66cc792..f2915f2 100644 (file)
@@ -56,8 +56,7 @@
 /* level of occupied TX descriptors under which we wake up TX process */
 #define MACB_TX_WAKEUP_THRESH(bp)      (3 * (bp)->tx_ring_size / 4)
 
-#define MACB_RX_INT_FLAGS      (MACB_BIT(RCOMP) | MACB_BIT(RXUBR)      \
-                                | MACB_BIT(ISR_ROVR))
+#define MACB_RX_INT_FLAGS      (MACB_BIT(RCOMP) | MACB_BIT(ISR_ROVR))
 #define MACB_TX_ERR_FLAGS      (MACB_BIT(ISR_TUND)                     \
                                        | MACB_BIT(ISR_RLE)             \
                                        | MACB_BIT(TXERR))
@@ -1270,7 +1269,7 @@ static int macb_poll(struct napi_struct *napi, int budget)
                                queue_writel(queue, ISR, MACB_BIT(RCOMP));
                        napi_reschedule(napi);
                } else {
-                       queue_writel(queue, IER, MACB_RX_INT_FLAGS);
+                       queue_writel(queue, IER, bp->rx_intr_mask);
                }
        }
 
@@ -1288,7 +1287,7 @@ static void macb_hresp_error_task(unsigned long data)
        u32 ctrl;
 
        for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
-               queue_writel(queue, IDR, MACB_RX_INT_FLAGS |
+               queue_writel(queue, IDR, bp->rx_intr_mask |
                                         MACB_TX_INT_FLAGS |
                                         MACB_BIT(HRESP));
        }
@@ -1318,7 +1317,7 @@ static void macb_hresp_error_task(unsigned long data)
 
                /* Enable interrupts */
                queue_writel(queue, IER,
-                            MACB_RX_INT_FLAGS |
+                            bp->rx_intr_mask |
                             MACB_TX_INT_FLAGS |
                             MACB_BIT(HRESP));
        }
@@ -1372,14 +1371,14 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
                            (unsigned int)(queue - bp->queues),
                            (unsigned long)status);
 
-               if (status & MACB_RX_INT_FLAGS) {
+               if (status & bp->rx_intr_mask) {
                        /* There's no point taking any more interrupts
                         * until we have processed the buffers. The
                         * scheduling call may fail if the poll routine
                         * is already scheduled, so disable interrupts
                         * now.
                         */
-                       queue_writel(queue, IDR, MACB_RX_INT_FLAGS);
+                       queue_writel(queue, IDR, bp->rx_intr_mask);
                        if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
                                queue_writel(queue, ISR, MACB_BIT(RCOMP));
 
@@ -1412,8 +1411,9 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
                /* There is a hardware issue under heavy load where DMA can
                 * stop, this causes endless "used buffer descriptor read"
                 * interrupts but it can be cleared by re-enabling RX. See
-                * the at91 manual, section 41.3.1 or the Zynq manual
-                * section 16.7.4 for details.
+                * the at91rm9200 manual, section 41.3.1 or the Zynq manual
+                * section 16.7.4 for details. RXUBR is only enabled for
+                * these two versions.
                 */
                if (status & MACB_BIT(RXUBR)) {
                        ctrl = macb_readl(bp, NCR);
@@ -1734,7 +1734,7 @@ static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *ndev)
                if (!nskb)
                        return -ENOMEM;
 
-               dev_kfree_skb_any(*skb);
+               dev_consume_skb_any(*skb);
                *skb = nskb;
        }
 
@@ -2259,7 +2259,7 @@ static void macb_init_hw(struct macb *bp)
 
                /* Enable interrupts */
                queue_writel(queue, IER,
-                            MACB_RX_INT_FLAGS |
+                            bp->rx_intr_mask |
                             MACB_TX_INT_FLAGS |
                             MACB_BIT(HRESP));
        }
@@ -3673,9 +3673,9 @@ static netdev_tx_t at91ether_start_xmit(struct sk_buff *skb,
                /* Store packet information (to free when Tx completed) */
                lp->skb = skb;
                lp->skb_length = skb->len;
-               lp->skb_physaddr = dma_map_single(NULL, skb->data, skb->len,
-                                                       DMA_TO_DEVICE);
-               if (dma_mapping_error(NULL, lp->skb_physaddr)) {
+               lp->skb_physaddr = dma_map_single(&lp->pdev->dev, skb->data,
+                                                 skb->len, DMA_TO_DEVICE);
+               if (dma_mapping_error(&lp->pdev->dev, lp->skb_physaddr)) {
                        dev_kfree_skb_any(skb);
                        dev->stats.tx_dropped++;
                        netdev_err(dev, "%s: DMA mapping error\n", __func__);
@@ -3763,9 +3763,9 @@ static irqreturn_t at91ether_interrupt(int irq, void *dev_id)
                        dev->stats.tx_errors++;
 
                if (lp->skb) {
-                       dev_kfree_skb_irq(lp->skb);
+                       dev_consume_skb_irq(lp->skb);
                        lp->skb = NULL;
-                       dma_unmap_single(NULL, lp->skb_physaddr,
+                       dma_unmap_single(&lp->pdev->dev, lp->skb_physaddr,
                                         lp->skb_length, DMA_TO_DEVICE);
                        dev->stats.tx_packets++;
                        dev->stats.tx_bytes += lp->skb_length;
@@ -3907,6 +3907,7 @@ static const struct macb_config sama5d4_config = {
 };
 
 static const struct macb_config emac_config = {
+       .caps = MACB_CAPS_NEEDS_RSTONUBR,
        .clk_init = at91ether_clk_init,
        .init = at91ether_init,
 };
@@ -3928,7 +3929,8 @@ static const struct macb_config zynqmp_config = {
 };
 
 static const struct macb_config zynq_config = {
-       .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_NO_GIGABIT_HALF,
+       .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_NO_GIGABIT_HALF |
+               MACB_CAPS_NEEDS_RSTONUBR,
        .dma_burst_length = 16,
        .clk_init = macb_clk_init,
        .init = macb_init,
@@ -3941,6 +3943,7 @@ static const struct of_device_id macb_dt_ids[] = {
        { .compatible = "cdns,np4-macb", .data = &np4_config },
        { .compatible = "cdns,pc302-gem", .data = &pc302gem_config },
        { .compatible = "cdns,gem", .data = &pc302gem_config },
+       { .compatible = "cdns,sam9x60-macb", .data = &at91sam9260_config },
        { .compatible = "atmel,sama5d2-gem", .data = &sama5d2_config },
        { .compatible = "atmel,sama5d3-gem", .data = &sama5d3_config },
        { .compatible = "atmel,sama5d3-macb", .data = &sama5d3macb_config },
@@ -4083,6 +4086,10 @@ static int macb_probe(struct platform_device *pdev)
                                                macb_dma_desc_get_size(bp);
        }
 
+       bp->rx_intr_mask = MACB_RX_INT_FLAGS;
+       if (bp->caps & MACB_CAPS_NEEDS_RSTONUBR)
+               bp->rx_intr_mask |= MACB_BIT(RXUBR);
+
        mac = of_get_mac_address(np);
        if (mac) {
                ether_addr_copy(bp->dev->dev_addr, mac);
index 5f03199..05f4a3b 100644 (file)
@@ -54,7 +54,6 @@ config CAVIUM_PTP
        tristate "Cavium PTP coprocessor as PTP clock"
        depends on 64BIT && PCI
        imply PTP_1588_CLOCK
-       default y
        ---help---
          This driver adds support for the Precision Time Protocol Clocks and
          Timestamping coprocessor (PTP) found on Cavium processors.
index 825a28e..e21bf37 100644 (file)
@@ -661,7 +661,8 @@ liquidio_push_packet(u32 octeon_id __attribute__((unused)),
                    (((rh->r_dh.encap_on) &&
                      (rh->r_dh.csum_verified & CNNIC_TUN_CSUM_VERIFIED)) ||
                     (!(rh->r_dh.encap_on) &&
-                     (rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED))))
+                     ((rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED) ==
+                       CNNIC_CSUM_VERIFIED))))
                        /* checksum has already been verified */
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                else
index 3d24133..e97e675 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/firmware.h>
 #include <net/vxlan.h>
 #include <linux/kthread.h>
-#include <net/switchdev.h>
 #include "liquidio_common.h"
 #include "octeon_droq.h"
 #include "octeon_iq.h"
@@ -3184,7 +3183,8 @@ static const struct devlink_ops liquidio_devlink_ops = {
 };
 
 static int
-lio_pf_switchdev_attr_get(struct net_device *dev, struct switchdev_attr *attr)
+liquidio_get_port_parent_id(struct net_device *dev,
+                           struct netdev_phys_item_id *ppid)
 {
        struct lio *lio = GET_LIO(dev);
        struct octeon_device *oct = lio->oct_dev;
@@ -3192,24 +3192,12 @@ lio_pf_switchdev_attr_get(struct net_device *dev, struct switchdev_attr *attr)
        if (oct->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
                return -EOPNOTSUPP;
 
-       switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = ETH_ALEN;
-               ether_addr_copy(attr->u.ppid.id,
-                               (void *)&lio->linfo.hw_addr + 2);
-               break;
-
-       default:
-               return -EOPNOTSUPP;
-       }
+       ppid->id_len = ETH_ALEN;
+       ether_addr_copy(ppid->id, (void *)&lio->linfo.hw_addr + 2);
 
        return 0;
 }
 
-static const struct switchdev_ops lio_pf_switchdev_ops = {
-       .switchdev_port_attr_get = lio_pf_switchdev_attr_get,
-};
-
 static int liquidio_get_vf_stats(struct net_device *netdev, int vfidx,
                                 struct ifla_vf_stats *vf_stats)
 {
@@ -3259,6 +3247,7 @@ static const struct net_device_ops lionetdevops = {
        .ndo_set_vf_trust       = liquidio_set_vf_trust,
        .ndo_set_vf_link_state  = liquidio_set_vf_link_state,
        .ndo_get_vf_stats       = liquidio_get_vf_stats,
+       .ndo_get_port_parent_id = liquidio_get_port_parent_id,
 };
 
 /** \brief Entry point for the liquidio module
@@ -3534,7 +3523,6 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
                 * netdev tasks.
                 */
                netdev->netdev_ops = &lionetdevops;
-               SWITCHDEV_SET_OPS(netdev, &lio_pf_switchdev_ops);
 
                retval = netif_set_real_num_rx_queues(netdev, num_oqueues);
                if (retval) {
index de61060..f3f2e71 100644 (file)
@@ -25,7 +25,6 @@
 #include "octeon_nic.h"
 #include "octeon_main.h"
 #include "octeon_network.h"
-#include <net/switchdev.h>
 #include "lio_vf_rep.h"
 
 static int lio_vf_rep_open(struct net_device *ndev);
@@ -38,6 +37,8 @@ static int lio_vf_rep_phys_port_name(struct net_device *dev,
 static void lio_vf_rep_get_stats64(struct net_device *dev,
                                   struct rtnl_link_stats64 *stats64);
 static int lio_vf_rep_change_mtu(struct net_device *ndev, int new_mtu);
+static int lio_vf_get_port_parent_id(struct net_device *dev,
+                                    struct netdev_phys_item_id *ppid);
 
 static const struct net_device_ops lio_vf_rep_ndev_ops = {
        .ndo_open = lio_vf_rep_open,
@@ -47,6 +48,7 @@ static const struct net_device_ops lio_vf_rep_ndev_ops = {
        .ndo_get_phys_port_name = lio_vf_rep_phys_port_name,
        .ndo_get_stats64 = lio_vf_rep_get_stats64,
        .ndo_change_mtu = lio_vf_rep_change_mtu,
+       .ndo_get_port_parent_id = lio_vf_get_port_parent_id,
 };
 
 static int
@@ -443,31 +445,19 @@ xmit_failed:
        return NETDEV_TX_OK;
 }
 
-static int
-lio_vf_rep_attr_get(struct net_device *dev, struct switchdev_attr *attr)
+static int lio_vf_get_port_parent_id(struct net_device *dev,
+                                    struct netdev_phys_item_id *ppid)
 {
        struct lio_vf_rep_desc *vf_rep = netdev_priv(dev);
        struct net_device *parent_ndev = vf_rep->parent_ndev;
        struct lio *lio = GET_LIO(parent_ndev);
 
-       switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = ETH_ALEN;
-               ether_addr_copy(attr->u.ppid.id,
-                               (void *)&lio->linfo.hw_addr + 2);
-               break;
-
-       default:
-               return -EOPNOTSUPP;
-       }
+       ppid->id_len = ETH_ALEN;
+       ether_addr_copy(ppid->id, (void *)&lio->linfo.hw_addr + 2);
 
        return 0;
 }
 
-static const struct switchdev_ops lio_vf_rep_switchdev_ops = {
-       .switchdev_port_attr_get        = lio_vf_rep_attr_get,
-};
-
 static void
 lio_vf_rep_fetch_stats(struct work_struct *work)
 {
@@ -524,7 +514,6 @@ lio_vf_rep_create(struct octeon_device *oct)
                ndev->min_mtu = LIO_MIN_MTU_SIZE;
                ndev->max_mtu = LIO_MAX_MTU_SIZE;
                ndev->netdev_ops = &lio_vf_rep_ndev_ops;
-               SWITCHDEV_SET_OPS(ndev, &lio_vf_rep_switchdev_ops);
 
                vf_rep = netdev_priv(ndev);
                memset(vf_rep, 0, sizeof(*vf_rep));
index 30de26e..47b5c8e 100644 (file)
@@ -585,8 +585,7 @@ static int alloc_rx_resources(struct sge *sge, struct sge_params *p)
                sizeof(struct cpl_rx_data) +
                sge->freelQ[!sge->jumbo_fl].dma_offset;
 
-               size = (16 * 1024) -
-                   SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+       size = (16 * 1024) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 
        sge->freelQ[sge->jumbo_fl].rx_buffer_size = size;
 
index 5701272..ce28820 100644 (file)
@@ -289,8 +289,7 @@ struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
        if (clipt_size < CLIPT_MIN_HASH_BUCKETS)
                return NULL;
 
-       ctbl = kvzalloc(sizeof(*ctbl) +
-                           clipt_size*sizeof(struct list_head), GFP_KERNEL);
+       ctbl = kvzalloc(struct_size(ctbl, hash_list, clipt_size), GFP_KERNEL);
        if (!ctbl)
                return NULL;
 
index 2d1ca92..b7b0eb1 100644 (file)
@@ -568,7 +568,7 @@ struct sge_rspq;
 struct port_info {
        struct adapter *adapter;
        u16    viid;
-       s16    xact_addr_filt;        /* index of exact MAC address filter */
+       int    xact_addr_filt;        /* index of exact MAC address filter */
        u16    rss_size;              /* size of VI's RSS table slice */
        s8     mdio_addr;
        enum fw_port_type port_type;
@@ -617,6 +617,7 @@ enum {                                 /* adapter flags */
        FW_OFLD_CONN       = (1 << 9),
        ROOT_NO_RELAXED_ORDERING = (1 << 10),
        SHUTTING_DOWN      = (1 << 11),
+       SGE_DBQ_TIMER      = (1 << 12),
 };
 
 enum {
@@ -756,6 +757,8 @@ struct sge_eth_txq {                /* state for an SGE Ethernet Tx queue */
 #ifdef CONFIG_CHELSIO_T4_DCB
        u8 dcb_prio;                /* DCB Priority bound to queue */
 #endif
+       u8 dbqt;                    /* SGE Doorbell Queue Timer in use */
+       unsigned int dbqtimerix;    /* SGE Doorbell Queue Timer Index */
        unsigned long tso;          /* # of TSO requests */
        unsigned long tx_cso;       /* # of Tx checksum offloads */
        unsigned long vlan_ins;     /* # of Tx VLAN insertions */
@@ -816,6 +819,8 @@ struct sge {
        u16 nqs_per_uld;            /* # of Rx queues per ULD */
        u16 timer_val[SGE_NTIMERS];
        u8 counter_val[SGE_NCOUNTERS];
+       u16 dbqtimer_tick;
+       u16 dbqtimer_val[SGE_NDBQTIMERS];
        u32 fl_pg_order;            /* large page allocation size */
        u32 stat_len;               /* length of status page at ring end */
        u32 pktshift;               /* padding between CPL & packet data */
@@ -860,6 +865,7 @@ struct doorbell_stats {
 struct hash_mac_addr {
        struct list_head list;
        u8 addr[ETH_ALEN];
+       unsigned int iface_mac;
 };
 
 struct uld_msix_bmap {
@@ -1401,7 +1407,7 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
                     rspq_flush_handler_t flush_handler, int cong);
 int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq,
                         struct net_device *dev, struct netdev_queue *netdevq,
-                        unsigned int iqid);
+                        unsigned int iqid, u8 dbqt);
 int t4_sge_alloc_ctrl_txq(struct adapter *adap, struct sge_ctrl_txq *txq,
                          struct net_device *dev, unsigned int iqid,
                          unsigned int cmplqid);
@@ -1414,6 +1420,8 @@ irqreturn_t t4_sge_intr_msix(int irq, void *cookie);
 int t4_sge_init(struct adapter *adap);
 void t4_sge_start(struct adapter *adap);
 void t4_sge_stop(struct adapter *adap);
+int t4_sge_eth_txq_egress_update(struct adapter *adap, struct sge_eth_txq *q,
+                                int maxreclaim);
 void cxgb4_set_ethtool_ops(struct net_device *netdev);
 int cxgb4_write_rss(const struct port_info *pi, const u16 *queues);
 enum cpl_tx_tnl_lso_type cxgb_encap_offload_supported(struct sk_buff *skb);
@@ -1820,6 +1828,8 @@ int t4_ctrl_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf,
 int t4_ofld_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf,
                    unsigned int vf, unsigned int eqid);
 int t4_sge_ctxt_flush(struct adapter *adap, unsigned int mbox, int ctxt_type);
+int t4_read_sge_dbqtimers(struct adapter *adap, unsigned int ndbqtimers,
+                         u16 *dbqtimers);
 void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl);
 int t4_update_port_info(struct port_info *pi);
 int t4_get_link_params(struct port_info *pi, unsigned int *link_okp,
index 7960435..65b8dc7 100644 (file)
@@ -932,11 +932,190 @@ static int get_adaptive_rx_setting(struct net_device *dev)
        return q->rspq.adaptive_rx;
 }
 
-static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
+/* Return the current global Adapter SGE Doorbell Queue Timer Tick for all
+ * Ethernet TX Queues.
+ */
+static int get_dbqtimer_tick(struct net_device *dev)
+{
+       struct port_info *pi = netdev_priv(dev);
+       struct adapter *adap = pi->adapter;
+
+       if (!(adap->flags & SGE_DBQ_TIMER))
+               return 0;
+
+       return adap->sge.dbqtimer_tick;
+}
+
+/* Return the SGE Doorbell Queue Timer Value for the Ethernet TX Queues
+ * associated with a Network Device.
+ */
+static int get_dbqtimer(struct net_device *dev)
+{
+       struct port_info *pi = netdev_priv(dev);
+       struct adapter *adap = pi->adapter;
+       struct sge_eth_txq *txq;
+
+       txq = &adap->sge.ethtxq[pi->first_qset];
+
+       if (!(adap->flags & SGE_DBQ_TIMER))
+               return 0;
+
+       /* all of the TX Queues use the same Timer Index */
+       return adap->sge.dbqtimer_val[txq->dbqtimerix];
+}
+
+/* Set the global Adapter SGE Doorbell Queue Timer Tick for all Ethernet TX
+ * Queues.  This is the fundamental "Tick" that sets the scale of values which
+ * can be used.  Individual Ethernet TX Queues index into a relatively small
+ * array of Tick Multipliers.  Changing the base Tick will thus change all of
+ * the resulting Timer Values associated with those multipliers for all
+ * Ethernet TX Queues.
+ */
+static int set_dbqtimer_tick(struct net_device *dev, int usecs)
+{
+       struct port_info *pi = netdev_priv(dev);
+       struct adapter *adap = pi->adapter;
+       struct sge *s = &adap->sge;
+       u32 param, val;
+       int ret;
+
+       if (!(adap->flags & SGE_DBQ_TIMER))
+               return 0;
+
+       /* return early if it's the same Timer Tick we're already using */
+       if (s->dbqtimer_tick == usecs)
+               return 0;
+
+       /* attempt to set the new Timer Tick value */
+       param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
+                FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DBQ_TIMERTICK));
+       val = usecs;
+       ret = t4_set_params(adap, adap->mbox, adap->pf, 0, 1, &param, &val);
+       if (ret)
+               return ret;
+       s->dbqtimer_tick = usecs;
+
+       /* if successful, reread resulting dependent Timer values */
+       ret = t4_read_sge_dbqtimers(adap, ARRAY_SIZE(s->dbqtimer_val),
+                                   s->dbqtimer_val);
+       return ret;
+}
+
+/* Set the SGE Doorbell Queue Timer Value for the Ethernet TX Queues
+ * associated with a Network Device.  There is a relatively small array of
+ * possible Timer Values so we need to pick the closest value available.
+ */
+static int set_dbqtimer(struct net_device *dev, int usecs)
+{
+       int qix, timerix, min_timerix, delta, min_delta;
+       struct port_info *pi = netdev_priv(dev);
+       struct adapter *adap = pi->adapter;
+       struct sge *s = &adap->sge;
+       struct sge_eth_txq *txq;
+       u32 param, val;
+       int ret;
+
+       if (!(adap->flags & SGE_DBQ_TIMER))
+               return 0;
+
+       /* Find the SGE Doorbell Timer Value that's closest to the requested
+        * value.
+        */
+       min_delta = INT_MAX;
+       min_timerix = 0;
+       for (timerix = 0; timerix < ARRAY_SIZE(s->dbqtimer_val); timerix++) {
+               delta = s->dbqtimer_val[timerix] - usecs;
+               if (delta < 0)
+                       delta = -delta;
+               if (delta < min_delta) {
+                       min_delta = delta;
+                       min_timerix = timerix;
+               }
+       }
+
+       /* Return early if it's the same Timer Index we're already using.
+        * We use the same Timer Index for all of the TX Queues for an
+        * interface so it's only necessary to check the first one.
+        */
+       txq = &s->ethtxq[pi->first_qset];
+       if (txq->dbqtimerix == min_timerix)
+               return 0;
+
+       for (qix = 0; qix < pi->nqsets; qix++, txq++) {
+               if (adap->flags & FULL_INIT_DONE) {
+                       param =
+                        (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
+                         FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DMAQ_EQ_TIMERIX) |
+                         FW_PARAMS_PARAM_YZ_V(txq->q.cntxt_id));
+                       val = min_timerix;
+                       ret = t4_set_params(adap, adap->mbox, adap->pf, 0,
+                                           1, &param, &val);
+                       if (ret)
+                               return ret;
+               }
+               txq->dbqtimerix = min_timerix;
+       }
+       return 0;
+}
+
+/* Set the global Adapter SGE Doorbell Queue Timer Tick for all Ethernet TX
+ * Queues and the Timer Value for the Ethernet TX Queues associated with a
+ * Network Device.  Since changing the global Tick changes all of the
+ * available Timer Values, we need to do this first before selecting the
+ * resulting closest Timer Value.  Moreover, since the Tick is global,
+ * changing it affects the Timer Values for all Network Devices on the
+ * adapter.  So, before changing the Tick, we grab all of the current Timer
+ * Values for other Network Devices on this Adapter and then attempt to select
+ * new Timer Values which are close to the old values ...
+ */
+static int set_dbqtimer_tickval(struct net_device *dev,
+                               int tick_usecs, int timer_usecs)
+{
+       struct port_info *pi = netdev_priv(dev);
+       struct adapter *adap = pi->adapter;
+       int timer[MAX_NPORTS];
+       unsigned int port;
+       int ret;
+
+       /* Grab the other adapter Network Interface current timers and fill in
+        * the new one for this Network Interface.
+        */
+       for_each_port(adap, port)
+               if (port == pi->port_id)
+                       timer[port] = timer_usecs;
+               else
+                       timer[port] = get_dbqtimer(adap->port[port]);
+
+       /* Change the global Tick first ... */
+       ret = set_dbqtimer_tick(dev, tick_usecs);
+       if (ret)
+               return ret;
+
+       /* ... and then set all of the Network Interface Timer Values ... */
+       for_each_port(adap, port) {
+               ret = set_dbqtimer(adap->port[port], timer[port]);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int set_coalesce(struct net_device *dev,
+                       struct ethtool_coalesce *coalesce)
 {
-       set_adaptive_rx_setting(dev, c->use_adaptive_rx_coalesce);
-       return set_rx_intr_params(dev, c->rx_coalesce_usecs,
-                                 c->rx_max_coalesced_frames);
+       int ret;
+
+       set_adaptive_rx_setting(dev, coalesce->use_adaptive_rx_coalesce);
+
+       ret = set_rx_intr_params(dev, coalesce->rx_coalesce_usecs,
+                                coalesce->rx_max_coalesced_frames);
+       if (ret)
+               return ret;
+
+       return set_dbqtimer_tickval(dev,
+                                   coalesce->tx_coalesce_usecs_irq,
+                                   coalesce->tx_coalesce_usecs);
 }
 
 static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
@@ -949,6 +1128,8 @@ static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
        c->rx_max_coalesced_frames = (rq->intr_params & QINTR_CNT_EN_F) ?
                adap->sge.counter_val[rq->pktcnt_idx] : 0;
        c->use_adaptive_rx_coalesce = get_adaptive_rx_setting(dev);
+       c->tx_coalesce_usecs_irq = get_dbqtimer_tick(dev);
+       c->tx_coalesce_usecs = get_dbqtimer(dev);
        return 0;
 }
 
index 73fc247..bcbac24 100644 (file)
@@ -433,6 +433,60 @@ static int set_rxmode(struct net_device *dev, int mtu, bool sleep_ok)
 }
 
 /**
+ *     cxgb4_change_mac - Update match filter for a MAC address.
+ *     @pi: the port_info
+ *     @viid: the VI id
+ *     @tcam_idx: TCAM index of existing filter for old value of MAC address,
+ *                or -1
+ *     @addr: the new MAC address value
+ *     @persist: whether a new MAC allocation should be persistent
+ *     @add_smt: if true also add the address to the HW SMT
+ *
+ *     Modifies an MPS filter and sets it to the new MAC address if
+ *     @tcam_idx >= 0, or adds the MAC address to a new filter if
+ *     @tcam_idx < 0. In the latter case the address is added persistently
+ *     if @persist is %true.
+ *     Addresses are programmed to hash region, if tcam runs out of entries.
+ *
+ */
+static int cxgb4_change_mac(struct port_info *pi, unsigned int viid,
+                           int *tcam_idx, const u8 *addr, bool persist,
+                           u8 *smt_idx)
+{
+       struct adapter *adapter = pi->adapter;
+       struct hash_mac_addr *entry, *new_entry;
+       int ret;
+
+       ret = t4_change_mac(adapter, adapter->mbox, viid,
+                           *tcam_idx, addr, persist, smt_idx);
+       /* We ran out of TCAM entries. try programming hash region. */
+       if (ret == -ENOMEM) {
+               /* If the MAC address to be updated is in the hash addr
+                * list, update it from the list
+                */
+               list_for_each_entry(entry, &adapter->mac_hlist, list) {
+                       if (entry->iface_mac) {
+                               ether_addr_copy(entry->addr, addr);
+                               goto set_hash;
+                       }
+               }
+               new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
+               if (!new_entry)
+                       return -ENOMEM;
+               ether_addr_copy(new_entry->addr, addr);
+               new_entry->iface_mac = true;
+               list_add_tail(&new_entry->list, &adapter->mac_hlist);
+set_hash:
+               ret = cxgb4_set_addr_hash(pi);
+       } else if (ret >= 0) {
+               *tcam_idx = ret;
+               ret = 0;
+       }
+
+       return ret;
+}
+
+/*
  *     link_start - enable a port
  *     @dev: the port to enable
  *
@@ -450,15 +504,9 @@ static int link_start(struct net_device *dev)
         */
        ret = t4_set_rxmode(pi->adapter, mb, pi->viid, dev->mtu, -1, -1, -1,
                            !!(dev->features & NETIF_F_HW_VLAN_CTAG_RX), true);
-       if (ret == 0) {
-               ret = t4_change_mac(pi->adapter, mb, pi->viid,
-                                   pi->xact_addr_filt, dev->dev_addr, true,
-                                   &pi->smt_idx);
-               if (ret >= 0) {
-                       pi->xact_addr_filt = ret;
-                       ret = 0;
-               }
-       }
+       if (ret == 0)
+               ret = cxgb4_change_mac(pi, pi->viid, &pi->xact_addr_filt,
+                                      dev->dev_addr, true, &pi->smt_idx);
        if (ret == 0)
                ret = t4_link_l1cfg(pi->adapter, mb, pi->tx_chan,
                                    &pi->link_cfg);
@@ -527,7 +575,7 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp,
                        struct sge_eth_txq *eq;
 
                        eq = container_of(txq, struct sge_eth_txq, q);
-                       netif_tx_wake_queue(eq->txq);
+                       t4_sge_eth_txq_egress_update(q->adap, eq, -1);
                } else {
                        struct sge_uld_txq *oq;
 
@@ -885,10 +933,13 @@ static int setup_sge_queues(struct adapter *adap)
                        q->rspq.idx = j;
                        memset(&q->stats, 0, sizeof(q->stats));
                }
-               for (j = 0; j < pi->nqsets; j++, t++) {
+
+               q = &s->ethrxq[pi->first_qset];
+               for (j = 0; j < pi->nqsets; j++, t++, q++) {
                        err = t4_sge_alloc_eth_txq(adap, t, dev,
                                        netdev_get_tx_queue(dev, j),
-                                       s->fw_evtq.cntxt_id);
+                                       q->rspq.cntxt_id,
+                                       !!(adap->flags & SGE_DBQ_TIMER));
                        if (err)
                                goto freeout;
                }
@@ -910,7 +961,7 @@ static int setup_sge_queues(struct adapter *adap)
        if (!is_t4(adap->params.chip)) {
                err = t4_sge_alloc_eth_txq(adap, &s->ptptxq, adap->port[0],
                                           netdev_get_tx_queue(adap->port[0], 0)
-                                          , s->fw_evtq.cntxt_id);
+                                          , s->fw_evtq.cntxt_id, false);
                if (err)
                        goto freeout;
        }
@@ -2839,9 +2890,8 @@ static int cxgb_set_mac_addr(struct net_device *dev, void *p)
        if (!is_valid_ether_addr(addr->sa_data))
                return -EADDRNOTAVAIL;
 
-       ret = t4_change_mac(pi->adapter, pi->adapter->pf, pi->viid,
-                           pi->xact_addr_filt, addr->sa_data, true,
-                           &pi->smt_idx);
+       ret = cxgb4_change_mac(pi, pi->viid, &pi->xact_addr_filt,
+                              addr->sa_data, true, &pi->smt_idx);
        if (ret < 0)
                return ret;
 
@@ -4278,6 +4328,24 @@ static int adap_init0(struct adapter *adap)
        if (ret < 0)
                goto bye;
 
+       /* Grab the SGE Doorbell Queue Timer values.  If successful, that
+        * indicates that the Firmware and Hardware support this.
+        */
+       params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
+                   FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DBQ_TIMERTICK));
+       ret = t4_query_params(adap, adap->mbox, adap->pf, 0,
+                             1, params, val);
+
+       if (!ret) {
+               adap->sge.dbqtimer_tick = val[0];
+               ret = t4_read_sge_dbqtimers(adap,
+                                           ARRAY_SIZE(adap->sge.dbqtimer_val),
+                                           adap->sge.dbqtimer_val);
+       }
+
+       if (!ret)
+               adap->flags |= SGE_DBQ_TIMER;
+
        if (is_bypass_device(adap->pdev->device))
                adap->params.bypass = 1;
 
index c116f96..82a8d19 100644 (file)
@@ -83,28 +83,23 @@ static void cxgb4_process_flow_match(struct net_device *dev,
                                     struct tc_cls_flower_offload *cls,
                                     struct ch_filter_specification *fs)
 {
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(cls);
        u16 addr_type = 0;
 
-       if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
-               struct flow_dissector_key_control *key =
-                       skb_flow_dissector_target(cls->dissector,
-                                                 FLOW_DISSECTOR_KEY_CONTROL,
-                                                 cls->key);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+               struct flow_match_control match;
 
-               addr_type = key->addr_type;
+               flow_rule_match_control(rule, &match);
+               addr_type = match.key->addr_type;
        }
 
-       if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *key =
-                       skb_flow_dissector_target(cls->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 cls->key);
-               struct flow_dissector_key_basic *mask =
-                       skb_flow_dissector_target(cls->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 cls->mask);
-               u16 ethtype_key = ntohs(key->n_proto);
-               u16 ethtype_mask = ntohs(mask->n_proto);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
+               u16 ethtype_key, ethtype_mask;
+
+               flow_rule_match_basic(rule, &match);
+               ethtype_key = ntohs(match.key->n_proto);
+               ethtype_mask = ntohs(match.mask->n_proto);
 
                if (ethtype_key == ETH_P_ALL) {
                        ethtype_key = 0;
@@ -116,115 +111,89 @@ static void cxgb4_process_flow_match(struct net_device *dev,
 
                fs->val.ethtype = ethtype_key;
                fs->mask.ethtype = ethtype_mask;
-               fs->val.proto = key->ip_proto;
-               fs->mask.proto = mask->ip_proto;
+               fs->val.proto = match.key->ip_proto;
+               fs->mask.proto = match.mask->ip_proto;
        }
 
        if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
-               struct flow_dissector_key_ipv4_addrs *key =
-                       skb_flow_dissector_target(cls->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                                 cls->key);
-               struct flow_dissector_key_ipv4_addrs *mask =
-                       skb_flow_dissector_target(cls->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                                 cls->mask);
+               struct flow_match_ipv4_addrs match;
+
+               flow_rule_match_ipv4_addrs(rule, &match);
                fs->type = 0;
-               memcpy(&fs->val.lip[0], &key->dst, sizeof(key->dst));
-               memcpy(&fs->val.fip[0], &key->src, sizeof(key->src));
-               memcpy(&fs->mask.lip[0], &mask->dst, sizeof(mask->dst));
-               memcpy(&fs->mask.fip[0], &mask->src, sizeof(mask->src));
+               memcpy(&fs->val.lip[0], &match.key->dst, sizeof(match.key->dst));
+               memcpy(&fs->val.fip[0], &match.key->src, sizeof(match.key->src));
+               memcpy(&fs->mask.lip[0], &match.mask->dst, sizeof(match.mask->dst));
+               memcpy(&fs->mask.fip[0], &match.mask->src, sizeof(match.mask->src));
 
                /* also initialize nat_lip/fip to same values */
-               memcpy(&fs->nat_lip[0], &key->dst, sizeof(key->dst));
-               memcpy(&fs->nat_fip[0], &key->src, sizeof(key->src));
-
+               memcpy(&fs->nat_lip[0], &match.key->dst, sizeof(match.key->dst));
+               memcpy(&fs->nat_fip[0], &match.key->src, sizeof(match.key->src));
        }
 
        if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
-               struct flow_dissector_key_ipv6_addrs *key =
-                       skb_flow_dissector_target(cls->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                                 cls->key);
-               struct flow_dissector_key_ipv6_addrs *mask =
-                       skb_flow_dissector_target(cls->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                                 cls->mask);
+               struct flow_match_ipv6_addrs match;
 
+               flow_rule_match_ipv6_addrs(rule, &match);
                fs->type = 1;
-               memcpy(&fs->val.lip[0], key->dst.s6_addr, sizeof(key->dst));
-               memcpy(&fs->val.fip[0], key->src.s6_addr, sizeof(key->src));
-               memcpy(&fs->mask.lip[0], mask->dst.s6_addr, sizeof(mask->dst));
-               memcpy(&fs->mask.fip[0], mask->src.s6_addr, sizeof(mask->src));
+               memcpy(&fs->val.lip[0], match.key->dst.s6_addr,
+                      sizeof(match.key->dst));
+               memcpy(&fs->val.fip[0], match.key->src.s6_addr,
+                      sizeof(match.key->src));
+               memcpy(&fs->mask.lip[0], match.mask->dst.s6_addr,
+                      sizeof(match.mask->dst));
+               memcpy(&fs->mask.fip[0], match.mask->src.s6_addr,
+                      sizeof(match.mask->src));
 
                /* also initialize nat_lip/fip to same values */
-               memcpy(&fs->nat_lip[0], key->dst.s6_addr, sizeof(key->dst));
-               memcpy(&fs->nat_fip[0], key->src.s6_addr, sizeof(key->src));
+               memcpy(&fs->nat_lip[0], match.key->dst.s6_addr,
+                      sizeof(match.key->dst));
+               memcpy(&fs->nat_fip[0], match.key->src.s6_addr,
+                      sizeof(match.key->src));
        }
 
-       if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
-               struct flow_dissector_key_ports *key, *mask;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+               struct flow_match_ports match;
 
-               key = skb_flow_dissector_target(cls->dissector,
-                                               FLOW_DISSECTOR_KEY_PORTS,
-                                               cls->key);
-               mask = skb_flow_dissector_target(cls->dissector,
-                                                FLOW_DISSECTOR_KEY_PORTS,
-                                                cls->mask);
-               fs->val.lport = cpu_to_be16(key->dst);
-               fs->mask.lport = cpu_to_be16(mask->dst);
-               fs->val.fport = cpu_to_be16(key->src);
-               fs->mask.fport = cpu_to_be16(mask->src);
+               flow_rule_match_ports(rule, &match);
+               fs->val.lport = cpu_to_be16(match.key->dst);
+               fs->mask.lport = cpu_to_be16(match.mask->dst);
+               fs->val.fport = cpu_to_be16(match.key->src);
+               fs->mask.fport = cpu_to_be16(match.mask->src);
 
                /* also initialize nat_lport/fport to same values */
-               fs->nat_lport = cpu_to_be16(key->dst);
-               fs->nat_fport = cpu_to_be16(key->src);
+               fs->nat_lport = cpu_to_be16(match.key->dst);
+               fs->nat_fport = cpu_to_be16(match.key->src);
        }
 
-       if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_IP)) {
-               struct flow_dissector_key_ip *key, *mask;
-
-               key = skb_flow_dissector_target(cls->dissector,
-                                               FLOW_DISSECTOR_KEY_IP,
-                                               cls->key);
-               mask = skb_flow_dissector_target(cls->dissector,
-                                                FLOW_DISSECTOR_KEY_IP,
-                                                cls->mask);
-               fs->val.tos = key->tos;
-               fs->mask.tos = mask->tos;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
+               struct flow_match_ip match;
+
+               flow_rule_match_ip(rule, &match);
+               fs->val.tos = match.key->tos;
+               fs->mask.tos = match.mask->tos;
        }
 
-       if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
-               struct flow_dissector_key_keyid *key, *mask;
-
-               key = skb_flow_dissector_target(cls->dissector,
-                                               FLOW_DISSECTOR_KEY_ENC_KEYID,
-                                               cls->key);
-               mask = skb_flow_dissector_target(cls->dissector,
-                                                FLOW_DISSECTOR_KEY_ENC_KEYID,
-                                                cls->mask);
-               fs->val.vni = be32_to_cpu(key->keyid);
-               fs->mask.vni = be32_to_cpu(mask->keyid);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+               struct flow_match_enc_keyid match;
+
+               flow_rule_match_enc_keyid(rule, &match);
+               fs->val.vni = be32_to_cpu(match.key->keyid);
+               fs->mask.vni = be32_to_cpu(match.mask->keyid);
                if (fs->mask.vni) {
                        fs->val.encap_vld = 1;
                        fs->mask.encap_vld = 1;
                }
        }
 
-       if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
-               struct flow_dissector_key_vlan *key, *mask;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+               struct flow_match_vlan match;
                u16 vlan_tci, vlan_tci_mask;
 
-               key = skb_flow_dissector_target(cls->dissector,
-                                               FLOW_DISSECTOR_KEY_VLAN,
-                                               cls->key);
-               mask = skb_flow_dissector_target(cls->dissector,
-                                                FLOW_DISSECTOR_KEY_VLAN,
-                                                cls->mask);
-               vlan_tci = key->vlan_id | (key->vlan_priority <<
-                                          VLAN_PRIO_SHIFT);
-               vlan_tci_mask = mask->vlan_id | (mask->vlan_priority <<
-                                                VLAN_PRIO_SHIFT);
+               flow_rule_match_vlan(rule, &match);
+               vlan_tci = match.key->vlan_id | (match.key->vlan_priority <<
+                                              VLAN_PRIO_SHIFT);
+               vlan_tci_mask = match.mask->vlan_id | (match.mask->vlan_priority <<
+                                                    VLAN_PRIO_SHIFT);
                fs->val.ivlan = vlan_tci;
                fs->mask.ivlan = vlan_tci_mask;
 
@@ -255,10 +224,12 @@ static void cxgb4_process_flow_match(struct net_device *dev,
 static int cxgb4_validate_flow_match(struct net_device *dev,
                                     struct tc_cls_flower_offload *cls)
 {
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(cls);
+       struct flow_dissector *dissector = rule->match.dissector;
        u16 ethtype_mask = 0;
        u16 ethtype_key = 0;
 
-       if (cls->dissector->used_keys &
+       if (dissector->used_keys &
            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
              BIT(FLOW_DISSECTOR_KEY_BASIC) |
              BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
@@ -268,36 +239,29 @@ static int cxgb4_validate_flow_match(struct net_device *dev,
              BIT(FLOW_DISSECTOR_KEY_VLAN) |
              BIT(FLOW_DISSECTOR_KEY_IP))) {
                netdev_warn(dev, "Unsupported key used: 0x%x\n",
-                           cls->dissector->used_keys);
+                           dissector->used_keys);
                return -EOPNOTSUPP;
        }
 
-       if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *key =
-                       skb_flow_dissector_target(cls->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 cls->key);
-               struct flow_dissector_key_basic *mask =
-                       skb_flow_dissector_target(cls->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 cls->mask);
-               ethtype_key = ntohs(key->n_proto);
-               ethtype_mask = ntohs(mask->n_proto);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
+
+               flow_rule_match_basic(rule, &match);
+               ethtype_key = ntohs(match.key->n_proto);
+               ethtype_mask = ntohs(match.mask->n_proto);
        }
 
-       if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_IP)) {
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
                u16 eth_ip_type = ethtype_key & ethtype_mask;
-               struct flow_dissector_key_ip *mask;
+               struct flow_match_ip match;
 
                if (eth_ip_type != ETH_P_IP && eth_ip_type != ETH_P_IPV6) {
                        netdev_err(dev, "IP Key supported only with IPv4/v6");
                        return -EINVAL;
                }
 
-               mask = skb_flow_dissector_target(cls->dissector,
-                                                FLOW_DISSECTOR_KEY_IP,
-                                                cls->mask);
-               if (mask->ttl) {
+               flow_rule_match_ip(rule, &match);
+               if (match.mask->ttl) {
                        netdev_warn(dev, "ttl match unsupported for offload");
                        return -EOPNOTSUPP;
                }
@@ -328,7 +292,7 @@ static void process_pedit_field(struct ch_filter_specification *fs, u32 val,
                                u32 mask, u32 offset, u8 htype)
 {
        switch (htype) {
-       case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
+       case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
                switch (offset) {
                case PEDIT_ETH_DMAC_31_0:
                        fs->newdmac = 1;
@@ -346,7 +310,7 @@ static void process_pedit_field(struct ch_filter_specification *fs, u32 val,
                        offload_pedit(fs, val, mask, ETH_SMAC_47_16);
                }
                break;
-       case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
+       case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
                switch (offset) {
                case PEDIT_IP4_SRC:
                        offload_pedit(fs, val, mask, IP4_SRC);
@@ -356,7 +320,7 @@ static void process_pedit_field(struct ch_filter_specification *fs, u32 val,
                }
                fs->nat_mode = NAT_MODE_ALL;
                break;
-       case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
+       case FLOW_ACT_MANGLE_HDR_TYPE_IP6:
                switch (offset) {
                case PEDIT_IP6_SRC_31_0:
                        offload_pedit(fs, val, mask, IP6_SRC_31_0);
@@ -384,7 +348,7 @@ static void process_pedit_field(struct ch_filter_specification *fs, u32 val,
                }
                fs->nat_mode = NAT_MODE_ALL;
                break;
-       case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
+       case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
                switch (offset) {
                case PEDIT_TCP_SPORT_DPORT:
                        if (~mask & PEDIT_TCP_UDP_SPORT_MASK)
@@ -397,7 +361,7 @@ static void process_pedit_field(struct ch_filter_specification *fs, u32 val,
                }
                fs->nat_mode = NAT_MODE_ALL;
                break;
-       case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
+       case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
                switch (offset) {
                case PEDIT_UDP_SPORT_DPORT:
                        if (~mask & PEDIT_TCP_UDP_SPORT_MASK)
@@ -416,56 +380,63 @@ static void cxgb4_process_flow_actions(struct net_device *in,
                                       struct tc_cls_flower_offload *cls,
                                       struct ch_filter_specification *fs)
 {
-       const struct tc_action *a;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(cls);
+       struct flow_action_entry *act;
        int i;
 
-       tcf_exts_for_each_action(i, a, cls->exts) {
-               if (is_tcf_gact_ok(a)) {
+       flow_action_for_each(i, act, &rule->action) {
+               switch (act->id) {
+               case FLOW_ACTION_ACCEPT:
                        fs->action = FILTER_PASS;
-               } else if (is_tcf_gact_shot(a)) {
+                       break;
+               case FLOW_ACTION_DROP:
                        fs->action = FILTER_DROP;
-               } else if (is_tcf_mirred_egress_redirect(a)) {
-                       struct net_device *out = tcf_mirred_dev(a);
+                       break;
+               case FLOW_ACTION_REDIRECT: {
+                       struct net_device *out = act->dev;
                        struct port_info *pi = netdev_priv(out);
 
                        fs->action = FILTER_SWITCH;
                        fs->eport = pi->port_id;
-               } else if (is_tcf_vlan(a)) {
-                       u32 vlan_action = tcf_vlan_action(a);
-                       u8 prio = tcf_vlan_push_prio(a);
-                       u16 vid = tcf_vlan_push_vid(a);
+                       }
+                       break;
+               case FLOW_ACTION_VLAN_POP:
+               case FLOW_ACTION_VLAN_PUSH:
+               case FLOW_ACTION_VLAN_MANGLE: {
+                       u8 prio = act->vlan.prio;
+                       u16 vid = act->vlan.vid;
                        u16 vlan_tci = (prio << VLAN_PRIO_SHIFT) | vid;
-
-                       switch (vlan_action) {
-                       case TCA_VLAN_ACT_POP:
+                       switch (act->id) {
+                       case FLOW_ACTION_VLAN_POP:
                                fs->newvlan |= VLAN_REMOVE;
                                break;
-                       case TCA_VLAN_ACT_PUSH:
+                       case FLOW_ACTION_VLAN_PUSH:
                                fs->newvlan |= VLAN_INSERT;
                                fs->vlan = vlan_tci;
                                break;
-                       case TCA_VLAN_ACT_MODIFY:
+                       case FLOW_ACTION_VLAN_MANGLE:
                                fs->newvlan |= VLAN_REWRITE;
                                fs->vlan = vlan_tci;
                                break;
                        default:
                                break;
                        }
-               } else if (is_tcf_pedit(a)) {
+                       }
+                       break;
+               case FLOW_ACTION_MANGLE: {
                        u32 mask, val, offset;
-                       int nkeys, i;
                        u8 htype;
 
-                       nkeys = tcf_pedit_nkeys(a);
-                       for (i = 0; i < nkeys; i++) {
-                               htype = tcf_pedit_htype(a, i);
-                               mask = tcf_pedit_mask(a, i);
-                               val = tcf_pedit_val(a, i);
-                               offset = tcf_pedit_offset(a, i);
+                       htype = act->mangle.htype;
+                       mask = act->mangle.mask;
+                       val = act->mangle.val;
+                       offset = act->mangle.offset;
 
-                               process_pedit_field(fs, val, mask, offset,
-                                                   htype);
+                       process_pedit_field(fs, val, mask, offset, htype);
                        }
+                       break;
+               default:
+                       break;
                }
        }
 }
@@ -484,101 +455,89 @@ static bool valid_l4_mask(u32 mask)
 }
 
 static bool valid_pedit_action(struct net_device *dev,
-                              const struct tc_action *a)
+                              const struct flow_action_entry *act)
 {
        u32 mask, offset;
-       u8 cmd, htype;
-       int nkeys, i;
-
-       nkeys = tcf_pedit_nkeys(a);
-       for (i = 0; i < nkeys; i++) {
-               htype = tcf_pedit_htype(a, i);
-               cmd = tcf_pedit_cmd(a, i);
-               mask = tcf_pedit_mask(a, i);
-               offset = tcf_pedit_offset(a, i);
-
-               if (cmd != TCA_PEDIT_KEY_EX_CMD_SET) {
-                       netdev_err(dev, "%s: Unsupported pedit cmd\n",
+       u8 htype;
+
+       htype = act->mangle.htype;
+       mask = act->mangle.mask;
+       offset = act->mangle.offset;
+
+       switch (htype) {
+       case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
+               switch (offset) {
+               case PEDIT_ETH_DMAC_31_0:
+               case PEDIT_ETH_DMAC_47_32_SMAC_15_0:
+               case PEDIT_ETH_SMAC_47_16:
+                       break;
+               default:
+                       netdev_err(dev, "%s: Unsupported pedit field\n",
                                   __func__);
                        return false;
                }
-
-               switch (htype) {
-               case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
-                       switch (offset) {
-                       case PEDIT_ETH_DMAC_31_0:
-                       case PEDIT_ETH_DMAC_47_32_SMAC_15_0:
-                       case PEDIT_ETH_SMAC_47_16:
-                               break;
-                       default:
-                               netdev_err(dev, "%s: Unsupported pedit field\n",
-                                          __func__);
-                               return false;
-                       }
-                       break;
-               case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
-                       switch (offset) {
-                       case PEDIT_IP4_SRC:
-                       case PEDIT_IP4_DST:
-                               break;
-                       default:
-                               netdev_err(dev, "%s: Unsupported pedit field\n",
-                                          __func__);
-                               return false;
-                       }
+               break;
+       case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
+               switch (offset) {
+               case PEDIT_IP4_SRC:
+               case PEDIT_IP4_DST:
                        break;
-               case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
-                       switch (offset) {
-                       case PEDIT_IP6_SRC_31_0:
-                       case PEDIT_IP6_SRC_63_32:
-                       case PEDIT_IP6_SRC_95_64:
-                       case PEDIT_IP6_SRC_127_96:
-                       case PEDIT_IP6_DST_31_0:
-                       case PEDIT_IP6_DST_63_32:
-                       case PEDIT_IP6_DST_95_64:
-                       case PEDIT_IP6_DST_127_96:
-                               break;
-                       default:
-                               netdev_err(dev, "%s: Unsupported pedit field\n",
-                                          __func__);
-                               return false;
-                       }
+               default:
+                       netdev_err(dev, "%s: Unsupported pedit field\n",
+                                  __func__);
+                       return false;
+               }
+               break;
+       case FLOW_ACT_MANGLE_HDR_TYPE_IP6:
+               switch (offset) {
+               case PEDIT_IP6_SRC_31_0:
+               case PEDIT_IP6_SRC_63_32:
+               case PEDIT_IP6_SRC_95_64:
+               case PEDIT_IP6_SRC_127_96:
+               case PEDIT_IP6_DST_31_0:
+               case PEDIT_IP6_DST_63_32:
+               case PEDIT_IP6_DST_95_64:
+               case PEDIT_IP6_DST_127_96:
                        break;
-               case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
-                       switch (offset) {
-                       case PEDIT_TCP_SPORT_DPORT:
-                               if (!valid_l4_mask(~mask)) {
-                                       netdev_err(dev, "%s: Unsupported mask for TCP L4 ports\n",
-                                                  __func__);
-                                       return false;
-                               }
-                               break;
-                       default:
-                               netdev_err(dev, "%s: Unsupported pedit field\n",
+               default:
+                       netdev_err(dev, "%s: Unsupported pedit field\n",
+                                  __func__);
+                       return false;
+               }
+               break;
+       case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
+               switch (offset) {
+               case PEDIT_TCP_SPORT_DPORT:
+                       if (!valid_l4_mask(~mask)) {
+                               netdev_err(dev, "%s: Unsupported mask for TCP L4 ports\n",
                                           __func__);
                                return false;
                        }
                        break;
-               case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
-                       switch (offset) {
-                       case PEDIT_UDP_SPORT_DPORT:
-                               if (!valid_l4_mask(~mask)) {
-                                       netdev_err(dev, "%s: Unsupported mask for UDP L4 ports\n",
-                                                  __func__);
-                                       return false;
-                               }
-                               break;
-                       default:
-                               netdev_err(dev, "%s: Unsupported pedit field\n",
+               default:
+                       netdev_err(dev, "%s: Unsupported pedit field\n",
+                                  __func__);
+                       return false;
+               }
+               break;
+       case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
+               switch (offset) {
+               case PEDIT_UDP_SPORT_DPORT:
+                       if (!valid_l4_mask(~mask)) {
+                               netdev_err(dev, "%s: Unsupported mask for UDP L4 ports\n",
                                           __func__);
                                return false;
                        }
                        break;
                default:
-                       netdev_err(dev, "%s: Unsupported pedit type\n",
+                       netdev_err(dev, "%s: Unsupported pedit field\n",
                                   __func__);
                        return false;
                }
+               break;
+       default:
+               netdev_err(dev, "%s: Unsupported pedit type\n", __func__);
+               return false;
        }
        return true;
 }
@@ -586,24 +545,26 @@ static bool valid_pedit_action(struct net_device *dev,
 static int cxgb4_validate_flow_actions(struct net_device *dev,
                                       struct tc_cls_flower_offload *cls)
 {
-       const struct tc_action *a;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(cls);
+       struct flow_action_entry *act;
        bool act_redir = false;
        bool act_pedit = false;
        bool act_vlan = false;
        int i;
 
-       tcf_exts_for_each_action(i, a, cls->exts) {
-               if (is_tcf_gact_ok(a)) {
-                       /* Do nothing */
-               } else if (is_tcf_gact_shot(a)) {
+       flow_action_for_each(i, act, &rule->action) {
+               switch (act->id) {
+               case FLOW_ACTION_ACCEPT:
+               case FLOW_ACTION_DROP:
                        /* Do nothing */
-               } else if (is_tcf_mirred_egress_redirect(a)) {
+                       break;
+               case FLOW_ACTION_REDIRECT: {
                        struct adapter *adap = netdev2adap(dev);
                        struct net_device *n_dev, *target_dev;
                        unsigned int i;
                        bool found = false;
 
-                       target_dev = tcf_mirred_dev(a);
+                       target_dev = act->dev;
                        for_each_port(adap, i) {
                                n_dev = adap->port[i];
                                if (target_dev == n_dev) {
@@ -621,15 +582,18 @@ static int cxgb4_validate_flow_actions(struct net_device *dev,
                                return -EINVAL;
                        }
                        act_redir = true;
-               } else if (is_tcf_vlan(a)) {
-                       u16 proto = be16_to_cpu(tcf_vlan_push_proto(a));
-                       u32 vlan_action = tcf_vlan_action(a);
+                       }
+                       break;
+               case FLOW_ACTION_VLAN_POP:
+               case FLOW_ACTION_VLAN_PUSH:
+               case FLOW_ACTION_VLAN_MANGLE: {
+                       u16 proto = be16_to_cpu(act->vlan.proto);
 
-                       switch (vlan_action) {
-                       case TCA_VLAN_ACT_POP:
+                       switch (act->id) {
+                       case FLOW_ACTION_VLAN_POP:
                                break;
-                       case TCA_VLAN_ACT_PUSH:
-                       case TCA_VLAN_ACT_MODIFY:
+                       case FLOW_ACTION_VLAN_PUSH:
+                       case FLOW_ACTION_VLAN_MANGLE:
                                if (proto != ETH_P_8021Q) {
                                        netdev_err(dev, "%s: Unsupported vlan proto\n",
                                                   __func__);
@@ -642,13 +606,17 @@ static int cxgb4_validate_flow_actions(struct net_device *dev,
                                return -EOPNOTSUPP;
                        }
                        act_vlan = true;
-               } else if (is_tcf_pedit(a)) {
-                       bool pedit_valid = valid_pedit_action(dev, a);
+                       }
+                       break;
+               case FLOW_ACTION_MANGLE: {
+                       bool pedit_valid = valid_pedit_action(dev, act);
 
                        if (!pedit_valid)
                                return -EOPNOTSUPP;
                        act_pedit = true;
-               } else {
+                       }
+                       break;
+               default:
                        netdev_err(dev, "%s: Unsupported action\n", __func__);
                        return -EOPNOTSUPP;
                }
@@ -843,9 +811,9 @@ int cxgb4_tc_flower_stats(struct net_device *dev,
        if (ofld_stats->packet_count != packets) {
                if (ofld_stats->prev_packet_count != packets)
                        ofld_stats->last_used = jiffies;
-               tcf_exts_stats_update(cls->exts, bytes - ofld_stats->byte_count,
-                                     packets - ofld_stats->packet_count,
-                                     ofld_stats->last_used);
+               flow_stats_update(&cls->stats, bytes - ofld_stats->byte_count,
+                                 packets - ofld_stats->packet_count,
+                                 ofld_stats->last_used);
 
                ofld_stats->packet_count = packets;
                ofld_stats->byte_count = bytes;
index c7d2b4d..02fc63f 100644 (file)
@@ -444,8 +444,7 @@ struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap)
        if (!max_tids)
                return NULL;
 
-       t = kvzalloc(sizeof(*t) +
-                        (max_tids * sizeof(struct cxgb4_link)), GFP_KERNEL);
+       t = kvzalloc(struct_size(t, table, max_tids), GFP_KERNEL);
        if (!t)
                return NULL;
 
index 52edb68..ba6c153 100644 (file)
@@ -478,7 +478,7 @@ struct sched_table *t4_init_sched(unsigned int sched_size)
        struct sched_table *s;
        unsigned int i;
 
-       s = kvzalloc(sizeof(*s) + sched_size * sizeof(struct sched_class), GFP_KERNEL);
+       s = kvzalloc(struct_size(s, tab, sched_size), GFP_KERNEL);
        if (!s)
                return NULL;
 
index fc0bc64..f18493f 100644 (file)
  * Max number of Tx descriptors we clean up at a time.  Should be modest as
  * freeing skbs isn't cheap and it happens while holding locks.  We just need
  * to free packets faster than they arrive, we eventually catch up and keep
- * the amortized cost reasonable.  Must be >= 2 * TXQ_STOP_THRES.
+ * the amortized cost reasonable.  Must be >= 2 * TXQ_STOP_THRES.  It should
+ * also match the CIDX Flush Threshold.
  */
-#define MAX_TX_RECLAIM 16
+#define MAX_TX_RECLAIM 32
 
 /*
  * Max number of Rx buffers we replenish at a time.  Again keep this modest,
@@ -401,31 +402,52 @@ static inline int reclaimable(const struct sge_txq *q)
 }
 
 /**
- *     cxgb4_reclaim_completed_tx - reclaims completed Tx descriptors
+ *     reclaim_completed_tx - reclaims completed TX Descriptors
  *     @adap: the adapter
  *     @q: the Tx queue to reclaim completed descriptors from
+ *     @maxreclaim: the maximum number of TX Descriptors to reclaim or -1
  *     @unmap: whether the buffers should be unmapped for DMA
  *
- *     Reclaims Tx descriptors that the SGE has indicated it has processed,
- *     and frees the associated buffers if possible.  Called with the Tx
- *     queue locked.
+ *     Reclaims Tx Descriptors that the SGE has indicated it has processed,
+ *     and frees the associated buffers if possible.  If @max == -1, then
+ *     we'll use a defaiult maximum.  Called with the TX Queue locked.
  */
-inline void cxgb4_reclaim_completed_tx(struct adapter *adap, struct sge_txq *q,
-                                       bool unmap)
+static inline int reclaim_completed_tx(struct adapter *adap, struct sge_txq *q,
+                                      int maxreclaim, bool unmap)
 {
-       int avail = reclaimable(q);
+       int reclaim = reclaimable(q);
 
-       if (avail) {
+       if (reclaim) {
                /*
                 * Limit the amount of clean up work we do at a time to keep
                 * the Tx lock hold time O(1).
                 */
-               if (avail > MAX_TX_RECLAIM)
-                       avail = MAX_TX_RECLAIM;
+               if (maxreclaim < 0)
+                       maxreclaim = MAX_TX_RECLAIM;
+               if (reclaim > maxreclaim)
+                       reclaim = maxreclaim;
 
-               free_tx_desc(adap, q, avail, unmap);
-               q->in_use -= avail;
+               free_tx_desc(adap, q, reclaim, unmap);
+               q->in_use -= reclaim;
        }
+
+       return reclaim;
+}
+
+/**
+ *     cxgb4_reclaim_completed_tx - reclaims completed Tx descriptors
+ *     @adap: the adapter
+ *     @q: the Tx queue to reclaim completed descriptors from
+ *     @unmap: whether the buffers should be unmapped for DMA
+ *
+ *     Reclaims Tx descriptors that the SGE has indicated it has processed,
+ *     and frees the associated buffers if possible.  Called with the Tx
+ *     queue locked.
+ */
+void cxgb4_reclaim_completed_tx(struct adapter *adap, struct sge_txq *q,
+                               bool unmap)
+{
+       (void)reclaim_completed_tx(adap, q, -1, unmap);
 }
 EXPORT_SYMBOL(cxgb4_reclaim_completed_tx);
 
@@ -1288,6 +1310,44 @@ static inline void t6_fill_tnl_lso(struct sk_buff *skb,
 }
 
 /**
+ *     t4_sge_eth_txq_egress_update - handle Ethernet TX Queue update
+ *     @adap: the adapter
+ *     @eq: the Ethernet TX Queue
+ *     @maxreclaim: the maximum number of TX Descriptors to reclaim or -1
+ *
+ *     We're typically called here to update the state of an Ethernet TX
+ *     Queue with respect to the hardware's progress in consuming the TX
+ *     Work Requests that we've put on that Egress Queue.  This happens
+ *     when we get Egress Queue Update messages and also prophylactically
+ *     in regular timer-based Ethernet TX Queue maintenance.
+ */
+int t4_sge_eth_txq_egress_update(struct adapter *adap, struct sge_eth_txq *eq,
+                                int maxreclaim)
+{
+       struct sge_txq *q = &eq->q;
+       unsigned int reclaimed;
+
+       if (!q->in_use || !__netif_tx_trylock(eq->txq))
+               return 0;
+
+       /* Reclaim pending completed TX Descriptors. */
+       reclaimed = reclaim_completed_tx(adap, &eq->q, maxreclaim, true);
+
+       /* If the TX Queue is currently stopped and there's now more than half
+        * the queue available, restart it.  Otherwise bail out since the rest
+        * of what we want do here is with the possibility of shipping any
+        * currently buffered Coalesced TX Work Request.
+        */
+       if (netif_tx_queue_stopped(eq->txq) && txq_avail(q) > (q->size / 2)) {
+               netif_tx_wake_queue(eq->txq);
+               eq->q.restarts++;
+       }
+
+       __netif_tx_unlock(eq->txq);
+       return reclaimed;
+}
+
+/**
  *     cxgb4_eth_xmit - add a packet to an Ethernet Tx queue
  *     @skb: the packet
  *     @dev: the egress net device
@@ -1357,7 +1417,7 @@ out_free: dev_kfree_skb_any(skb);
        }
        skb_tx_timestamp(skb);
 
-       cxgb4_reclaim_completed_tx(adap, &q->q, true);
+       reclaim_completed_tx(adap, &q->q, -1, true);
        cntrl = TXPKT_L4CSUM_DIS_F | TXPKT_IPCSUM_DIS_F;
 
 #ifdef CONFIG_CHELSIO_T4_FCOE
@@ -1400,8 +1460,25 @@ out_free:        dev_kfree_skb_any(skb);
 
        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
+                * packet, we'll be below our "stop threshold" so stop the TX
+                * Queue now and schedule a request for an SGE Egress Queue
+                * Update message. The queue will get started later on when
+                * the firmware processes this Work Request and sends us an
+                * Egress Queue Status Update message indicating that space
+                * has opened up.
+                */
                eth_txq_stop(q);
-               wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F;
+
+               /* If we're using the SGE Doorbell Queue Timer facility, we
+                * don't need to ask the Firmware to send us Egress Queue CIDX
+                * Updates: the Hardware will do this automatically.  And
+                * since we send the Ingress Queue CIDX Updates to the
+                * corresponding Ethernet Response Queue, we'll get them very
+                * quickly.
+                */
+               if (!q->dbqt)
+                       wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F;
        }
 
        wr = (void *)&q->q.desc[q->q.pidx];
@@ -1671,7 +1748,7 @@ static netdev_tx_t cxgb4_vf_eth_xmit(struct sk_buff *skb,
        /* Take this opportunity to reclaim any TX Descriptors whose DMA
         * transfers have completed.
         */
-       cxgb4_reclaim_completed_tx(adapter, &txq->q, true);
+       reclaim_completed_tx(adapter, &txq->q, -1, true);
 
        /* Calculate the number of flits and TX Descriptors we're going to
         * need along with how many TX Descriptors will be left over after
@@ -1715,7 +1792,16 @@ 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 we're using the SGE Doorbell Queue Timer facility, we
+                * don't need to ask the Firmware to send us Egress Queue CIDX
+                * Updates: the Hardware will do this automatically.  And
+                * since we send the Ingress Queue CIDX Updates to the
+                * corresponding Ethernet Response Queue, we'll get them very
+                * quickly.
+                */
+               if (!txq->dbqt)
+                       wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F;
        }
 
        /* Start filling in our Work Request.  Note that we do _not_ handle
@@ -2794,6 +2880,74 @@ static int t4_tx_hststamp(struct adapter *adapter, struct sk_buff *skb,
 }
 
 /**
+ *     t4_tx_completion_handler - handle CPL_SGE_EGR_UPDATE messages
+ *     @rspq: Ethernet RX Response Queue associated with Ethernet TX Queue
+ *     @rsp: Response Entry pointer into Response Queue
+ *     @gl: Gather List pointer
+ *
+ *     For adapters which support the SGE Doorbell Queue Timer facility,
+ *     we configure the Ethernet TX Queues to send CIDX Updates to the
+ *     Associated Ethernet RX Response Queue with CPL_SGE_EGR_UPDATE
+ *     messages.  This adds a small load to PCIe Link RX bandwidth and,
+ *     potentially, higher CPU Interrupt load, but allows us to respond
+ *     much more quickly to the CIDX Updates.  This is important for
+ *     Upper Layer Software which isn't willing to have a large amount
+ *     of TX Data outstanding before receiving DMA Completions.
+ */
+static void t4_tx_completion_handler(struct sge_rspq *rspq,
+                                    const __be64 *rsp,
+                                    const struct pkt_gl *gl)
+{
+       u8 opcode = ((const struct rss_header *)rsp)->opcode;
+       struct port_info *pi = netdev_priv(rspq->netdev);
+       struct adapter *adapter = rspq->adap;
+       struct sge *s = &adapter->sge;
+       struct sge_eth_txq *txq;
+
+       /* skip RSS header */
+       rsp++;
+
+       /* FW can send EGR_UPDATEs encapsulated in a CPL_FW4_MSG.
+        */
+       if (unlikely(opcode == CPL_FW4_MSG &&
+                    ((const struct cpl_fw4_msg *)rsp)->type ==
+                                                       FW_TYPE_RSSCPL)) {
+               rsp++;
+               opcode = ((const struct rss_header *)rsp)->opcode;
+               rsp++;
+       }
+
+       if (unlikely(opcode != CPL_SGE_EGR_UPDATE)) {
+               pr_info("%s: unexpected FW4/CPL %#x on Rx queue\n",
+                       __func__, opcode);
+               return;
+       }
+
+       txq = &s->ethtxq[pi->first_qset + rspq->idx];
+
+       /* We've got the Hardware Consumer Index Update in the Egress Update
+        * message.  If we're using the SGE Doorbell Queue Timer mechanism,
+        * 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 (txq->dbqt) {
+               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);
+}
+
+/**
  *     t4_ethrx_handler - process an ingress ethernet packet
  *     @q: the response queue that received the packet
  *     @rsp: the response queue descriptor holding the RX_PKT message
@@ -2816,6 +2970,15 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
        struct port_info *pi;
        int ret = 0;
 
+       /* If we're looking at TX Queue CIDX Update, handle that separately
+        * and return.
+        */
+       if (unlikely((*(u8 *)rsp == CPL_FW4_MSG) ||
+                    (*(u8 *)rsp == CPL_SGE_EGR_UPDATE))) {
+               t4_tx_completion_handler(q, rsp, si);
+               return 0;
+       }
+
        if (unlikely(*(u8 *)rsp == cpl_trace_pkt))
                return handle_trace_pkt(q->adap, si);
 
@@ -3289,10 +3452,10 @@ done:
 
 static void sge_tx_timer_cb(struct timer_list *t)
 {
-       unsigned long m;
-       unsigned int i, budget;
        struct adapter *adap = from_timer(adap, t, sge.tx_timer);
        struct sge *s = &adap->sge;
+       unsigned long m, period;
+       unsigned int i, budget;
 
        for (i = 0; i < BITS_TO_LONGS(s->egr_sz); i++)
                for (m = s->txq_maperr[i]; m; m &= m - 1) {
@@ -3320,29 +3483,29 @@ static void sge_tx_timer_cb(struct timer_list *t)
        budget = MAX_TIMER_TX_RECLAIM;
        i = s->ethtxq_rover;
        do {
-               struct sge_eth_txq *q = &s->ethtxq[i];
-
-               if (q->q.in_use &&
-                   time_after_eq(jiffies, q->txq->trans_start + HZ / 100) &&
-                   __netif_tx_trylock(q->txq)) {
-                       int avail = reclaimable(&q->q);
-
-                       if (avail) {
-                               if (avail > budget)
-                                       avail = budget;
-
-                               free_tx_desc(adap, &q->q, avail, true);
-                               q->q.in_use -= avail;
-                               budget -= avail;
-                       }
-                       __netif_tx_unlock(q->txq);
-               }
+               budget -= t4_sge_eth_txq_egress_update(adap, &s->ethtxq[i],
+                                                      budget);
+               if (!budget)
+                       break;
 
                if (++i >= s->ethqsets)
                        i = 0;
-       } while (budget && i != s->ethtxq_rover);
+       } while (i != s->ethtxq_rover);
        s->ethtxq_rover = i;
-       mod_timer(&s->tx_timer, jiffies + (budget ? TX_QCHECK_PERIOD : 2));
+
+       if (budget == 0) {
+               /* If we found too many reclaimable packets schedule a timer
+                * in the near future to continue where we left off.
+                */
+               period = 2;
+       } else {
+               /* We reclaimed all reclaimable TX Descriptors, so reschedule
+                * at the normal period.
+                */
+               period = TX_QCHECK_PERIOD;
+       }
+
+       mod_timer(&s->tx_timer, jiffies + period);
 }
 
 /**
@@ -3421,7 +3584,8 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
                                                        :  FW_IQ_IQTYPE_OFLD));
 
        if (fl) {
-               enum chip_type chip = CHELSIO_CHIP_VERSION(adap->params.chip);
+               unsigned int chip_ver =
+                       CHELSIO_CHIP_VERSION(adap->params.chip);
 
                /* Allocate the ring for the hardware free list (with space
                 * for its status page) along with the associated software
@@ -3459,10 +3623,10 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
                 * the smaller 64-byte value there).
                 */
                c.fl0dcaen_to_fl0cidxfthresh =
-                       htons(FW_IQ_CMD_FL0FBMIN_V(chip <= CHELSIO_T5 ?
+                       htons(FW_IQ_CMD_FL0FBMIN_V(chip_ver <= CHELSIO_T5 ?
                                                   FETCHBURSTMIN_128B_X :
-                                                  FETCHBURSTMIN_64B_X) |
-                             FW_IQ_CMD_FL0FBMAX_V((chip <= CHELSIO_T5) ?
+                                                  FETCHBURSTMIN_64B_T6_X) |
+                             FW_IQ_CMD_FL0FBMAX_V((chip_ver <= CHELSIO_T5) ?
                                                   FETCHBURSTMAX_512B_X :
                                                   FETCHBURSTMAX_256B_X));
                c.fl0size = htons(flsz);
@@ -3584,14 +3748,24 @@ static void init_txq(struct adapter *adap, struct sge_txq *q, unsigned int id)
        adap->sge.egr_map[id - adap->sge.egr_start] = q;
 }
 
+/**
+ *     t4_sge_alloc_eth_txq - allocate an Ethernet TX Queue
+ *     @adap: the adapter
+ *     @txq: the SGE Ethernet TX Queue to initialize
+ *     @dev: the Linux Network Device
+ *     @netdevq: the corresponding Linux TX Queue
+ *     @iqid: the Ingress Queue to which to deliver CIDX Update messages
+ *     @dbqt: whether this TX Queue will use the SGE Doorbell Queue Timers
+ */
 int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq,
                         struct net_device *dev, struct netdev_queue *netdevq,
-                        unsigned int iqid)
+                        unsigned int iqid, u8 dbqt)
 {
-       int ret, nentries;
-       struct fw_eq_eth_cmd c;
-       struct sge *s = &adap->sge;
+       unsigned int chip_ver = CHELSIO_CHIP_VERSION(adap->params.chip);
        struct port_info *pi = netdev_priv(dev);
+       struct sge *s = &adap->sge;
+       struct fw_eq_eth_cmd c;
+       int ret, nentries;
 
        /* Add status entries */
        nentries = txq->q.size + s->stat_len / sizeof(struct tx_desc);
@@ -3610,19 +3784,47 @@ int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq,
                            FW_EQ_ETH_CMD_VFN_V(0));
        c.alloc_to_len16 = htonl(FW_EQ_ETH_CMD_ALLOC_F |
                                 FW_EQ_ETH_CMD_EQSTART_F | FW_LEN16(c));
-       c.viid_pkd = htonl(FW_EQ_ETH_CMD_AUTOEQUEQE_F |
-                          FW_EQ_ETH_CMD_VIID_V(pi->viid));
+
+       /* For TX Ethernet Queues using the SGE Doorbell Queue Timer
+        * mechanism, we use Ingress Queue messages for Hardware Consumer
+        * Index Updates on the TX Queue.  Otherwise we have the Hardware
+        * write the CIDX Updates into the Status Page at the end of the
+        * TX Queue.
+        */
+       c.autoequiqe_to_viid = htonl((dbqt
+                                     ? 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(dbqt
+                                                ? 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));
+
+       /* Note that the CIDX Flush Threshold should match MAX_TX_RECLAIM. */
        c.dcaen_to_eqsize =
-               htonl(FW_EQ_ETH_CMD_FBMIN_V(FETCHBURSTMIN_64B_X) |
+               htonl(FW_EQ_ETH_CMD_FBMIN_V(chip_ver <= CHELSIO_T5
+                                           ? FETCHBURSTMIN_64B_X
+                                           : 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_EQSIZE_V(nentries));
+
        c.eqaddr = cpu_to_be64(txq->q.phys_addr);
 
+       /* If we're using the SGE Doorbell Queue Timer mechanism, pass in the
+        * currently configured Timer Index.  THis can be changed later via an
+        * ethtool -C tx-usecs {Timer Val} command.  Note that the SGE
+        * Doorbell Queue mode is currently automatically enabled in the
+        * Firmware by setting either AUTOEQUEQE or AUTOEQUIQE ...
+        */
+       if (dbqt)
+               c.timeren_timerix =
+                       cpu_to_be32(FW_EQ_ETH_CMD_TIMEREN_F |
+                                   FW_EQ_ETH_CMD_TIMERIX_V(txq->dbqtimerix));
+
        ret = t4_wr_mbox(adap, adap->mbox, &c, sizeof(c), &c);
        if (ret) {
                kfree(txq->q.sdesc);
@@ -3639,6 +3841,8 @@ int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq,
        txq->txq = netdevq;
        txq->tso = txq->tx_cso = txq->vlan_ins = 0;
        txq->mapping_err = 0;
+       txq->dbqt = dbqt;
+
        return 0;
 }
 
@@ -3646,10 +3850,11 @@ int t4_sge_alloc_ctrl_txq(struct adapter *adap, struct sge_ctrl_txq *txq,
                          struct net_device *dev, unsigned int iqid,
                          unsigned int cmplqid)
 {
-       int ret, nentries;
-       struct fw_eq_ctrl_cmd c;
-       struct sge *s = &adap->sge;
+       unsigned int chip_ver = CHELSIO_CHIP_VERSION(adap->params.chip);
        struct port_info *pi = netdev_priv(dev);
+       struct sge *s = &adap->sge;
+       struct fw_eq_ctrl_cmd c;
+       int ret, nentries;
 
        /* Add status entries */
        nentries = txq->q.size + s->stat_len / sizeof(struct tx_desc);
@@ -3673,7 +3878,9 @@ int t4_sge_alloc_ctrl_txq(struct adapter *adap, struct sge_ctrl_txq *txq,
                      FW_EQ_CTRL_CMD_PCIECHN_V(pi->tx_chan) |
                      FW_EQ_CTRL_CMD_FETCHRO_F | FW_EQ_CTRL_CMD_IQID_V(iqid));
        c.dcaen_to_eqsize =
-               htonl(FW_EQ_CTRL_CMD_FBMIN_V(FETCHBURSTMIN_64B_X) |
+               htonl(FW_EQ_CTRL_CMD_FBMIN_V(chip_ver <= CHELSIO_T5
+                                            ? FETCHBURSTMIN_64B_X
+                                            : FETCHBURSTMIN_64B_T6_X) |
                      FW_EQ_CTRL_CMD_FBMAX_V(FETCHBURSTMAX_512B_X) |
                      FW_EQ_CTRL_CMD_CIDXFTHRESH_V(CIDXFLUSHTHRESH_32_X) |
                      FW_EQ_CTRL_CMD_EQSIZE_V(nentries));
@@ -3713,6 +3920,7 @@ int t4_sge_alloc_uld_txq(struct adapter *adap, struct sge_uld_txq *txq,
                         struct net_device *dev, unsigned int iqid,
                         unsigned int uld_type)
 {
+       unsigned int chip_ver = CHELSIO_CHIP_VERSION(adap->params.chip);
        int ret, nentries;
        struct fw_eq_ofld_cmd c;
        struct sge *s = &adap->sge;
@@ -3743,7 +3951,9 @@ int t4_sge_alloc_uld_txq(struct adapter *adap, struct sge_uld_txq *txq,
                      FW_EQ_OFLD_CMD_PCIECHN_V(pi->tx_chan) |
                      FW_EQ_OFLD_CMD_FETCHRO_F | FW_EQ_OFLD_CMD_IQID_V(iqid));
        c.dcaen_to_eqsize =
-               htonl(FW_EQ_OFLD_CMD_FBMIN_V(FETCHBURSTMIN_64B_X) |
+               htonl(FW_EQ_OFLD_CMD_FBMIN_V(chip_ver <= CHELSIO_T5
+                                            ? FETCHBURSTMIN_64B_X
+                                            : FETCHBURSTMIN_64B_T6_X) |
                      FW_EQ_OFLD_CMD_FBMAX_V(FETCHBURSTMAX_512B_X) |
                      FW_EQ_OFLD_CMD_CIDXFTHRESH_V(CIDXFLUSHTHRESH_32_X) |
                      FW_EQ_OFLD_CMD_EQSIZE_V(nentries));
index 7b2207a..eaf1fb7 100644 (file)
@@ -47,8 +47,7 @@ struct smt_data *t4_init_smt(void)
 
        smt_size = SMT_SIZE;
 
-       s = kvzalloc(sizeof(*s) + smt_size * sizeof(struct smt_entry),
-                    GFP_KERNEL);
+       s = kvzalloc(struct_size(s, smtab, smt_size), GFP_KERNEL);
        if (!s)
                return NULL;
        s->smt_size = smt_size;
index c5e5466..27af347 100644 (file)
@@ -6713,6 +6713,47 @@ int t4_sge_ctxt_flush(struct adapter *adap, unsigned int mbox, int ctxt_type)
 }
 
 /**
+ *     t4_read_sge_dbqtimers - reag SGE Doorbell Queue Timer values
+ *     @adap - the adapter
+ *     @ndbqtimers: size of the provided SGE Doorbell Queue Timer table
+ *     @dbqtimers: SGE Doorbell Queue Timer table
+ *
+ *     Reads the SGE Doorbell Queue Timer values into the provided table.
+ *     Returns 0 on success (Firmware and Hardware support this feature),
+ *     an error on failure.
+ */
+int t4_read_sge_dbqtimers(struct adapter *adap, unsigned int ndbqtimers,
+                         u16 *dbqtimers)
+{
+       int ret, dbqtimerix;
+
+       ret = 0;
+       dbqtimerix = 0;
+       while (dbqtimerix < ndbqtimers) {
+               int nparams, param;
+               u32 params[7], vals[7];
+
+               nparams = ndbqtimers - dbqtimerix;
+               if (nparams > ARRAY_SIZE(params))
+                       nparams = ARRAY_SIZE(params);
+
+               for (param = 0; param < nparams; param++)
+                       params[param] =
+                         (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
+                          FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DBQ_TIMER) |
+                          FW_PARAMS_PARAM_Y_V(dbqtimerix + param));
+               ret = t4_query_params(adap, adap->mbox, adap->pf, 0,
+                                     nparams, params, vals);
+               if (ret)
+                       break;
+
+               for (param = 0; param < nparams; param++)
+                       dbqtimers[dbqtimerix++] = vals[param];
+       }
+       return ret;
+}
+
+/**
  *      t4_fw_hello - establish communication with FW
  *      @adap: the adapter
  *      @mbox: mailbox to use for the FW command
index 361d503..002fc62 100644 (file)
@@ -91,6 +91,7 @@ enum {
        SGE_CTXT_SIZE = 24,       /* size of SGE context */
        SGE_NTIMERS = 6,          /* # of interrupt holdoff timer values */
        SGE_NCOUNTERS = 4,        /* # of interrupt packet counter values */
+       SGE_NDBQTIMERS = 8,       /* # of Doorbell Queue Timer values */
        SGE_MAX_IQ_SIZE = 65520,
 
        SGE_TIMER_RSTRT_CNTR = 6, /* restart RX packet threshold counter */
index bf7325f..0c53734 100644 (file)
@@ -218,6 +218,7 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN
        CH_PCI_ID_TABLE_FENTRY(0x6088), /* Custom T62100-CR */
        CH_PCI_ID_TABLE_FENTRY(0x6089), /* Custom T62100-KR */
        CH_PCI_ID_TABLE_FENTRY(0x608a), /* Custom T62100-CR */
+       CH_PCI_ID_TABLE_FENTRY(0x608b), /* Custom T6225-CR */
 CH_PCI_DEVICE_ID_TABLE_DEFINE_END;
 
 #endif /* __T4_PCI_ID_TBL_H__ */
index f6558cb..eb1aa82 100644 (file)
 #define FETCHBURSTMIN_64B_X            2
 #define FETCHBURSTMIN_128B_X           3
 
+/* T6 and later use a single-bit encoding for FetchBurstMin */
+#define FETCHBURSTMIN_64B_T6_X         0
+#define FETCHBURSTMIN_128B_T6_X                1
+
 #define FETCHBURSTMAX_256B_X           2
 #define FETCHBURSTMAX_512B_X           3
 
+#define HOSTFCMODE_INGRESS_QUEUE_X     1
 #define HOSTFCMODE_STATUS_PAGE_X       2
 
 #define CIDXFLUSHTHRESH_32_X           5
+#define CIDXFLUSHTHRESH_128_X          7
 
 #define UPDATEDELIVERY_INTERRUPT_X     1
 
index 1d9b3e1..631f166 100644 (file)
@@ -1254,6 +1254,8 @@ enum fw_params_param_dev {
        FW_PARAMS_PARAM_DEV_RDMA_WRITE_WITH_IMM = 0x21,
        FW_PARAMS_PARAM_DEV_RI_WRITE_CMPL_WR    = 0x24,
        FW_PARAMS_PARAM_DEV_OPAQUE_VIID_SMT_EXTN = 0x27,
+       FW_PARAMS_PARAM_DEV_DBQ_TIMER   = 0x29,
+       FW_PARAMS_PARAM_DEV_DBQ_TIMERTICK = 0x2A,
 };
 
 /*
@@ -1322,6 +1324,7 @@ enum fw_params_param_dmaq {
        FW_PARAMS_PARAM_DMAQ_EQ_CMPLIQID_CTRL = 0x11,
        FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH = 0x12,
        FW_PARAMS_PARAM_DMAQ_EQ_DCBPRIO_ETH = 0x13,
+       FW_PARAMS_PARAM_DMAQ_EQ_TIMERIX = 0x15,
        FW_PARAMS_PARAM_DMAQ_CONM_CTXT = 0x20,
 };
 
@@ -1751,8 +1754,8 @@ struct fw_eq_eth_cmd {
        __be32 fetchszm_to_iqid;
        __be32 dcaen_to_eqsize;
        __be64 eqaddr;
-       __be32 viid_pkd;
-       __be32 r8_lo;
+       __be32 autoequiqe_to_viid;
+       __be32 timeren_timerix;
        __be64 r9;
 };
 
@@ -1847,6 +1850,10 @@ struct fw_eq_eth_cmd {
 #define FW_EQ_ETH_CMD_EQSIZE_S         0
 #define FW_EQ_ETH_CMD_EQSIZE_V(x)      ((x) << FW_EQ_ETH_CMD_EQSIZE_S)
 
+#define FW_EQ_ETH_CMD_AUTOEQUIQE_S     31
+#define FW_EQ_ETH_CMD_AUTOEQUIQE_V(x)  ((x) << FW_EQ_ETH_CMD_AUTOEQUIQE_S)
+#define FW_EQ_ETH_CMD_AUTOEQUIQE_F     FW_EQ_ETH_CMD_AUTOEQUIQE_V(1U)
+
 #define FW_EQ_ETH_CMD_AUTOEQUEQE_S     30
 #define FW_EQ_ETH_CMD_AUTOEQUEQE_V(x)  ((x) << FW_EQ_ETH_CMD_AUTOEQUEQE_S)
 #define FW_EQ_ETH_CMD_AUTOEQUEQE_F     FW_EQ_ETH_CMD_AUTOEQUEQE_V(1U)
@@ -1854,6 +1861,19 @@ struct fw_eq_eth_cmd {
 #define FW_EQ_ETH_CMD_VIID_S   16
 #define FW_EQ_ETH_CMD_VIID_V(x)        ((x) << FW_EQ_ETH_CMD_VIID_S)
 
+#define FW_EQ_ETH_CMD_TIMEREN_S                3
+#define FW_EQ_ETH_CMD_TIMEREN_M                0x1
+#define FW_EQ_ETH_CMD_TIMEREN_V(x)     ((x) << FW_EQ_ETH_CMD_TIMEREN_S)
+#define FW_EQ_ETH_CMD_TIMEREN_G(x)     \
+    (((x) >> FW_EQ_ETH_CMD_TIMEREN_S) & FW_EQ_ETH_CMD_TIMEREN_M)
+#define FW_EQ_ETH_CMD_TIMEREN_F        FW_EQ_ETH_CMD_TIMEREN_V(1U)
+
+#define FW_EQ_ETH_CMD_TIMERIX_S                0
+#define FW_EQ_ETH_CMD_TIMERIX_M                0x7
+#define FW_EQ_ETH_CMD_TIMERIX_V(x)     ((x) << FW_EQ_ETH_CMD_TIMERIX_S)
+#define FW_EQ_ETH_CMD_TIMERIX_G(x)     \
+    (((x) >> FW_EQ_ETH_CMD_TIMERIX_S) & FW_EQ_ETH_CMD_TIMERIX_M)
+
 struct fw_eq_ctrl_cmd {
        __be32 op_to_vfn;
        __be32 alloc_to_len16;
index a844296..9125ddd 100644 (file)
@@ -36,8 +36,8 @@
 #define __T4FW_VERSION_H__
 
 #define T4FW_VERSION_MAJOR 0x01
-#define T4FW_VERSION_MINOR 0x14
-#define T4FW_VERSION_MICRO 0x08
+#define T4FW_VERSION_MINOR 0x16
+#define T4FW_VERSION_MICRO 0x09
 #define T4FW_VERSION_BUILD 0x00
 
 #define T4FW_MIN_VERSION_MAJOR 0x01
@@ -45,8 +45,8 @@
 #define T4FW_MIN_VERSION_MICRO 0x00
 
 #define T5FW_VERSION_MAJOR 0x01
-#define T5FW_VERSION_MINOR 0x14
-#define T5FW_VERSION_MICRO 0x08
+#define T5FW_VERSION_MINOR 0x16
+#define T5FW_VERSION_MICRO 0x09
 #define T5FW_VERSION_BUILD 0x00
 
 #define T5FW_MIN_VERSION_MAJOR 0x00
@@ -54,8 +54,8 @@
 #define T5FW_MIN_VERSION_MICRO 0x00
 
 #define T6FW_VERSION_MAJOR 0x01
-#define T6FW_VERSION_MINOR 0x14
-#define T6FW_VERSION_MICRO 0x08
+#define T6FW_VERSION_MINOR 0x16
+#define T6FW_VERSION_MICRO 0x09
 #define T6FW_VERSION_BUILD 0x00
 
 #define T6FW_MIN_VERSION_MAJOR 0x00
index 5883f09..26f48a1 100644 (file)
@@ -94,7 +94,7 @@ struct port_info {
        struct adapter *adapter;        /* our adapter */
        u32 vlan_id;                    /* vlan id for VST */
        u16 viid;                       /* virtual interface ID */
-       s16 xact_addr_filt;             /* index of our MAC address filter */
+       int xact_addr_filt;             /* index of our MAC address filter */
        u16 rss_size;                   /* size of VI's RSS table slice */
        u8 pidx;                        /* index into adapter port[] */
        s8 mdio_addr;
@@ -352,6 +352,7 @@ struct sge {
 struct hash_mac_addr {
        struct list_head list;
        u8 addr[ETH_ALEN];
+       unsigned int iface_mac;
 };
 
 struct mbox_list {
index 2fab87e..3300b69 100644 (file)
@@ -236,6 +236,73 @@ void t4vf_os_portmod_changed(struct adapter *adapter, int pidx)
                         "inserted\n", dev->name, pi->mod_type);
 }
 
+static int cxgb4vf_set_addr_hash(struct port_info *pi)
+{
+       struct adapter *adapter = pi->adapter;
+       u64 vec = 0;
+       bool ucast = false;
+       struct hash_mac_addr *entry;
+
+       /* Calculate the hash vector for the updated list and program it */
+       list_for_each_entry(entry, &adapter->mac_hlist, list) {
+               ucast |= is_unicast_ether_addr(entry->addr);
+               vec |= (1ULL << hash_mac_addr(entry->addr));
+       }
+       return t4vf_set_addr_hash(adapter, pi->viid, ucast, vec, false);
+}
+
+/**
+ *     cxgb4vf_change_mac - Update match filter for a MAC address.
+ *     @pi: the port_info
+ *     @viid: the VI id
+ *     @tcam_idx: TCAM index of existing filter for old value of MAC address,
+ *                or -1
+ *     @addr: the new MAC address value
+ *     @persist: whether a new MAC allocation should be persistent
+ *     @add_smt: if true also add the address to the HW SMT
+ *
+ *     Modifies an MPS filter and sets it to the new MAC address if
+ *     @tcam_idx >= 0, or adds the MAC address to a new filter if
+ *     @tcam_idx < 0. In the latter case the address is added persistently
+ *     if @persist is %true.
+ *     Addresses are programmed to hash region, if tcam runs out of entries.
+ *
+ */
+static int cxgb4vf_change_mac(struct port_info *pi, unsigned int viid,
+                             int *tcam_idx, const u8 *addr, bool persistent)
+{
+       struct hash_mac_addr *new_entry, *entry;
+       struct adapter *adapter = pi->adapter;
+       int ret;
+
+       ret = t4vf_change_mac(adapter, viid, *tcam_idx, addr, persistent);
+       /* We ran out of TCAM entries. try programming hash region. */
+       if (ret == -ENOMEM) {
+               /* If the MAC address to be updated is in the hash addr
+                * list, update it from the list
+                */
+               list_for_each_entry(entry, &adapter->mac_hlist, list) {
+                       if (entry->iface_mac) {
+                               ether_addr_copy(entry->addr, addr);
+                               goto set_hash;
+                       }
+               }
+               new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
+               if (!new_entry)
+                       return -ENOMEM;
+               ether_addr_copy(new_entry->addr, addr);
+               new_entry->iface_mac = true;
+               list_add_tail(&new_entry->list, &adapter->mac_hlist);
+set_hash:
+               ret = cxgb4vf_set_addr_hash(pi);
+       } else if (ret >= 0) {
+               *tcam_idx = ret;
+               ret = 0;
+       }
+
+       return ret;
+}
+
 /*
  * Net device operations.
  * ======================
@@ -259,14 +326,10 @@ static int link_start(struct net_device *dev)
         */
        ret = t4vf_set_rxmode(pi->adapter, pi->viid, dev->mtu, -1, -1, -1, 1,
                              true);
-       if (ret == 0) {
-               ret = t4vf_change_mac(pi->adapter, pi->viid,
-                                     pi->xact_addr_filt, dev->dev_addr, true);
-               if (ret >= 0) {
-                       pi->xact_addr_filt = ret;
-                       ret = 0;
-               }
-       }
+       if (ret == 0)
+               ret = cxgb4vf_change_mac(pi, pi->viid,
+                                        &pi->xact_addr_filt,
+                                        dev->dev_addr, true);
 
        /*
         * We don't need to actually "start the link" itself since the
@@ -791,6 +854,13 @@ static int cxgb4vf_open(struct net_device *dev)
                        return err;
        }
 
+       /* It's possible that the basic port information could have
+        * changed since we first read it.
+        */
+       err = t4vf_update_port_info(pi);
+       if (err < 0)
+               return err;
+
        /*
         * Note that this interface is up and start everything up ...
         */
@@ -863,21 +933,6 @@ static struct net_device_stats *cxgb4vf_get_stats(struct net_device *dev)
        return ns;
 }
 
-static inline int cxgb4vf_set_addr_hash(struct port_info *pi)
-{
-       struct adapter *adapter = pi->adapter;
-       u64 vec = 0;
-       bool ucast = false;
-       struct hash_mac_addr *entry;
-
-       /* Calculate the hash vector for the updated list and program it */
-       list_for_each_entry(entry, &adapter->mac_hlist, list) {
-               ucast |= is_unicast_ether_addr(entry->addr);
-               vec |= (1ULL << hash_mac_addr(entry->addr));
-       }
-       return t4vf_set_addr_hash(adapter, pi->viid, ucast, vec, false);
-}
-
 static int cxgb4vf_mac_sync(struct net_device *netdev, const u8 *mac_addr)
 {
        struct port_info *pi = netdev_priv(netdev);
@@ -1159,13 +1214,12 @@ static int cxgb4vf_set_mac_addr(struct net_device *dev, void *_addr)
        if (!is_valid_ether_addr(addr->sa_data))
                return -EADDRNOTAVAIL;
 
-       ret = t4vf_change_mac(pi->adapter, pi->viid, pi->xact_addr_filt,
-                             addr->sa_data, true);
+       ret = cxgb4vf_change_mac(pi, pi->viid, &pi->xact_addr_filt,
+                                addr->sa_data, true);
        if (ret < 0)
                return ret;
 
        memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
-       pi->xact_addr_filt = ret;
        return 0;
 }
 
@@ -1354,7 +1408,7 @@ static void fw_caps_to_lmm(enum fw_port_type port_type,
        case FW_PORT_TYPE_CR4_QSFP:
                SET_LMM(FIBRE);
                FW_CAPS_TO_LMM(SPEED_1G,  1000baseT_Full);
-               FW_CAPS_TO_LMM(SPEED_10G, 10000baseSR_Full);
+               FW_CAPS_TO_LMM(SPEED_10G, 10000baseKR_Full);
                FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full);
                FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full);
                FW_CAPS_TO_LMM(SPEED_50G, 50000baseCR2_Full);
@@ -1365,6 +1419,13 @@ static void fw_caps_to_lmm(enum fw_port_type port_type,
                break;
        }
 
+       if (fw_caps & FW_PORT_CAP32_FEC_V(FW_PORT_CAP32_FEC_M)) {
+               FW_CAPS_TO_LMM(FEC_RS, FEC_RS);
+               FW_CAPS_TO_LMM(FEC_BASER_RS, FEC_BASER);
+       } else {
+               SET_LMM(FEC_NONE);
+       }
+
        FW_CAPS_TO_LMM(ANEG, Autoneg);
        FW_CAPS_TO_LMM(802_3_PAUSE, Pause);
        FW_CAPS_TO_LMM(802_3_ASM_DIR, Asym_Pause);
index 1d534f0..11d2ba0 100644 (file)
@@ -2268,7 +2268,7 @@ int t4vf_sge_alloc_rxq(struct adapter *adapter, struct sge_rspq *rspq,
        cmd.iqaddr = cpu_to_be64(rspq->phys_addr);
 
        if (fl) {
-               enum chip_type chip =
+               unsigned int chip_ver =
                        CHELSIO_CHIP_VERSION(adapter->params.chip);
                /*
                 * Allocate the ring for the hardware free list (with space
@@ -2319,10 +2319,10 @@ int t4vf_sge_alloc_rxq(struct adapter *adapter, struct sge_rspq *rspq,
                 */
                cmd.fl0dcaen_to_fl0cidxfthresh =
                        cpu_to_be16(
-                               FW_IQ_CMD_FL0FBMIN_V(chip <= CHELSIO_T5 ?
-                                                    FETCHBURSTMIN_128B_X :
-                                                    FETCHBURSTMIN_64B_X) |
-                               FW_IQ_CMD_FL0FBMAX_V((chip <= CHELSIO_T5) ?
+                               FW_IQ_CMD_FL0FBMIN_V(chip_ver <= CHELSIO_T5
+                                                    ? FETCHBURSTMIN_128B_X
+                                                    : FETCHBURSTMIN_64B_T6_X) |
+                               FW_IQ_CMD_FL0FBMAX_V((chip_ver <= CHELSIO_T5) ?
                                                     FETCHBURSTMAX_512B_X :
                                                     FETCHBURSTMAX_256B_X));
                cmd.fl0size = cpu_to_be16(flsz);
@@ -2411,10 +2411,11 @@ int t4vf_sge_alloc_eth_txq(struct adapter *adapter, struct sge_eth_txq *txq,
                           struct net_device *dev, struct netdev_queue *devq,
                           unsigned int iqid)
 {
+       unsigned int chip_ver = CHELSIO_CHIP_VERSION(adapter->params.chip);
+       struct port_info *pi = netdev_priv(dev);
+       struct fw_eq_eth_cmd cmd, rpl;
        struct sge *s = &adapter->sge;
        int ret, nentries;
-       struct fw_eq_eth_cmd cmd, rpl;
-       struct port_info *pi = netdev_priv(dev);
 
        /*
         * Calculate the size of the hardware TX Queue (including the Status
@@ -2448,17 +2449,19 @@ int t4vf_sge_alloc_eth_txq(struct adapter *adapter, struct sge_eth_txq *txq,
        cmd.alloc_to_len16 = cpu_to_be32(FW_EQ_ETH_CMD_ALLOC_F |
                                         FW_EQ_ETH_CMD_EQSTART_F |
                                         FW_LEN16(cmd));
-       cmd.viid_pkd = cpu_to_be32(FW_EQ_ETH_CMD_AUTOEQUEQE_F |
-                                  FW_EQ_ETH_CMD_VIID_V(pi->viid));
+       cmd.autoequiqe_to_viid = cpu_to_be32(FW_EQ_ETH_CMD_AUTOEQUEQE_F |
+                                            FW_EQ_ETH_CMD_VIID_V(pi->viid));
        cmd.fetchszm_to_iqid =
                cpu_to_be32(FW_EQ_ETH_CMD_HOSTFCMODE_V(SGE_HOSTFCMODE_STPG) |
                            FW_EQ_ETH_CMD_PCIECHN_V(pi->port_id) |
                            FW_EQ_ETH_CMD_IQID_V(iqid));
        cmd.dcaen_to_eqsize =
-               cpu_to_be32(FW_EQ_ETH_CMD_FBMIN_V(SGE_FETCHBURSTMIN_64B) |
-                           FW_EQ_ETH_CMD_FBMAX_V(SGE_FETCHBURSTMAX_512B) |
+               cpu_to_be32(FW_EQ_ETH_CMD_FBMIN_V(chip_ver <= CHELSIO_T5
+                                                 ? FETCHBURSTMIN_64B_X
+                                                 : FETCHBURSTMIN_64B_T6_X) |
+                           FW_EQ_ETH_CMD_FBMAX_V(FETCHBURSTMAX_512B_X) |
                            FW_EQ_ETH_CMD_CIDXFTHRESH_V(
-                                               SGE_CIDXFLUSHTHRESH_32) |
+                                               CIDXFLUSHTHRESH_32_X) |
                            FW_EQ_ETH_CMD_EQSIZE_V(nentries));
        cmd.eqaddr = cpu_to_be64(txq->q.phys_addr);
 
index 60641e2..9a7f70d 100644 (file)
@@ -1434,7 +1434,8 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
                 * csum is correct or is zero.
                 */
                if ((netdev->features & NETIF_F_RXCSUM) && !csum_not_calc &&
-                   tcp_udp_csum_ok && ipv4_csum_ok && outer_csum_ok) {
+                   tcp_udp_csum_ok && outer_csum_ok &&
+                   (ipv4_csum_ok || ipv6)) {
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                        skb->csum_level = encap;
                }
index 13430f7..f1a2da1 100644 (file)
@@ -585,7 +585,7 @@ static void de_tx (struct de_private *de)
                                netif_dbg(de, tx_done, de->dev,
                                          "tx done, slot %d\n", tx_tail);
                        }
-                       dev_kfree_skb_irq(skb);
+                       dev_consume_skb_irq(skb);
                }
 
 next:
index 1812f49..ba0a69b 100644 (file)
@@ -224,9 +224,7 @@ subsequent_board:
                        return;
                }
 
-               mtable = kmalloc(sizeof(struct mediatable) +
-                                count * sizeof(struct medialeaf),
-                                GFP_KERNEL);
+               mtable = kmalloc(struct_size(mtable, mleaf, count), GFP_KERNEL);
                if (mtable == NULL)
                        return;                         /* Horrible, impossible failure. */
                last_mediatable = tp->mtable = mtable;
index d8d423f..cfcdfee 100644 (file)
@@ -843,9 +843,9 @@ rio_free_tx (struct net_device *dev, int irq)
                                  desc_to_dma(&np->tx_ring[entry]),
                                  skb->len, PCI_DMA_TODEVICE);
                if (irq)
-                       dev_kfree_skb_irq (skb);
+                       dev_consume_skb_irq(skb);
                else
-                       dev_kfree_skb (skb);
+                       dev_kfree_skb(skb);
 
                np->tx_skbuff[entry] = NULL;
                entry = (entry + 1) % TX_RING_SIZE;
index 1a27176..4a37a69 100644 (file)
@@ -1193,7 +1193,6 @@ static irqreturn_t intr_handler(int irq, void *dev_instance)
        int handled = 0;
        int i;
 
-
        do {
                int intr_status = ioread16(ioaddr + IntrStatus);
                iowrite16(intr_status, ioaddr + IntrStatus);
@@ -1286,7 +1285,7 @@ static irqreturn_t intr_handler(int irq, void *dev_instance)
                                dma_unmap_single(&np->pci_dev->dev,
                                        le32_to_cpu(np->tx_ring[entry].frag[0].addr),
                                        skb->len, DMA_TO_DEVICE);
-                               dev_kfree_skb_irq (np->tx_skbuff[entry]);
+                               dev_consume_skb_irq(np->tx_skbuff[entry]);
                                np->tx_skbuff[entry] = NULL;
                                np->tx_ring[entry].frag[0].addr = 0;
                                np->tx_ring[entry].frag[0].length = 0;
@@ -1305,7 +1304,7 @@ static irqreturn_t intr_handler(int irq, void *dev_instance)
                                dma_unmap_single(&np->pci_dev->dev,
                                        le32_to_cpu(np->tx_ring[entry].frag[0].addr),
                                        skb->len, DMA_TO_DEVICE);
-                               dev_kfree_skb_irq (np->tx_skbuff[entry]);
+                               dev_consume_skb_irq(np->tx_skbuff[entry]);
                                np->tx_skbuff[entry] = NULL;
                                np->tx_ring[entry].frag[0].addr = 0;
                                np->tx_ring[entry].frag[0].length = 0;
index ae55da6..c24fd56 100644 (file)
@@ -1531,7 +1531,7 @@ static irqreturn_t intr_handler(int irq, void *dev_instance)
                        /* Free the original skb. */
                        pci_unmap_single(np->pci_dev, np->cur_tx->buffer,
                                np->cur_tx->skbuff->len, PCI_DMA_TODEVICE);
-                       dev_kfree_skb_irq(np->cur_tx->skbuff);
+                       dev_consume_skb_irq(np->cur_tx->skbuff);
                        np->cur_tx->skbuff = NULL;
                        --np->really_tx_count;
                        if (np->cur_tx->control & TXLD) {
index 6249711..bdee441 100644 (file)
@@ -501,7 +501,7 @@ static int dpaa_get_ts_info(struct net_device *net_dev,
        struct device_node *mac_node = dev->of_node;
        struct device_node *fman_node = NULL, *ptp_node = NULL;
        struct platform_device *ptp_dev = NULL;
-       struct qoriq_ptp *ptp = NULL;
+       struct ptp_qoriq *ptp = NULL;
 
        info->phc_index = -1;
 
index 04925c7..87777b0 100644 (file)
@@ -86,16 +86,16 @@ static void free_rx_fd(struct dpaa2_eth_priv *priv,
        for (i = 1; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) {
                addr = dpaa2_sg_get_addr(&sgt[i]);
                sg_vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr);
-               dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
-                                DMA_BIDIRECTIONAL);
+               dma_unmap_page(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
+                              DMA_BIDIRECTIONAL);
 
-               skb_free_frag(sg_vaddr);
+               free_pages((unsigned long)sg_vaddr, 0);
                if (dpaa2_sg_is_final(&sgt[i]))
                        break;
        }
 
 free_buf:
-       skb_free_frag(vaddr);
+       free_pages((unsigned long)vaddr, 0);
 }
 
 /* Build a linear skb based on a single-buffer frame descriptor */
@@ -109,7 +109,7 @@ static struct sk_buff *build_linear_skb(struct dpaa2_eth_channel *ch,
 
        ch->buf_count--;
 
-       skb = build_skb(fd_vaddr, DPAA2_ETH_SKB_SIZE);
+       skb = build_skb(fd_vaddr, DPAA2_ETH_RX_BUF_RAW_SIZE);
        if (unlikely(!skb))
                return NULL;
 
@@ -144,19 +144,19 @@ static struct sk_buff *build_frag_skb(struct dpaa2_eth_priv *priv,
                /* Get the address and length from the S/G entry */
                sg_addr = dpaa2_sg_get_addr(sge);
                sg_vaddr = dpaa2_iova_to_virt(priv->iommu_domain, sg_addr);
-               dma_unmap_single(dev, sg_addr, DPAA2_ETH_RX_BUF_SIZE,
-                                DMA_BIDIRECTIONAL);
+               dma_unmap_page(dev, sg_addr, DPAA2_ETH_RX_BUF_SIZE,
+                              DMA_BIDIRECTIONAL);
 
                sg_length = dpaa2_sg_get_len(sge);
 
                if (i == 0) {
                        /* We build the skb around the first data buffer */
-                       skb = build_skb(sg_vaddr, DPAA2_ETH_SKB_SIZE);
+                       skb = build_skb(sg_vaddr, DPAA2_ETH_RX_BUF_RAW_SIZE);
                        if (unlikely(!skb)) {
                                /* Free the first SG entry now, since we already
                                 * unmapped it and obtained the virtual address
                                 */
-                               skb_free_frag(sg_vaddr);
+                               free_pages((unsigned long)sg_vaddr, 0);
 
                                /* We still need to subtract the buffers used
                                 * by this FD from our software counter
@@ -211,9 +211,9 @@ static void free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, int count)
 
        for (i = 0; i < count; i++) {
                vaddr = dpaa2_iova_to_virt(priv->iommu_domain, buf_array[i]);
-               dma_unmap_single(dev, buf_array[i], DPAA2_ETH_RX_BUF_SIZE,
-                                DMA_BIDIRECTIONAL);
-               skb_free_frag(vaddr);
+               dma_unmap_page(dev, buf_array[i], DPAA2_ETH_RX_BUF_SIZE,
+                              DMA_BIDIRECTIONAL);
+               free_pages((unsigned long)vaddr, 0);
        }
 }
 
@@ -264,9 +264,7 @@ static int xdp_enqueue(struct dpaa2_eth_priv *priv, struct dpaa2_fd *fd,
 
        fq = &priv->fq[queue_id];
        for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
-               err = dpaa2_io_service_enqueue_qd(fq->channel->dpio,
-                                                 priv->tx_qdid, 0,
-                                                 fq->tx_qdbin, fd);
+               err = priv->enqueue(priv, fq, fd, 0);
                if (err != -EBUSY)
                        break;
        }
@@ -378,16 +376,16 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
                        return;
                }
 
-               dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
-                                DMA_BIDIRECTIONAL);
+               dma_unmap_page(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
+                              DMA_BIDIRECTIONAL);
                skb = build_linear_skb(ch, fd, vaddr);
        } else if (fd_format == dpaa2_fd_sg) {
                WARN_ON(priv->xdp_prog);
 
-               dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
-                                DMA_BIDIRECTIONAL);
+               dma_unmap_page(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
+                              DMA_BIDIRECTIONAL);
                skb = build_frag_skb(priv, ch, buf_data);
-               skb_free_frag(vaddr);
+               free_pages((unsigned long)vaddr, 0);
                percpu_extras->rx_sg_frames++;
                percpu_extras->rx_sg_bytes += dpaa2_fd_get_len(fd);
        } else {
@@ -657,7 +655,7 @@ static int build_single_fd(struct dpaa2_eth_priv *priv,
  * dpaa2_eth_tx().
  */
 static void free_tx_fd(const struct dpaa2_eth_priv *priv,
-                      const struct dpaa2_fd *fd)
+                      const struct dpaa2_fd *fd, bool in_napi)
 {
        struct device *dev = priv->net_dev->dev.parent;
        dma_addr_t fd_addr;
@@ -712,7 +710,7 @@ static void free_tx_fd(const struct dpaa2_eth_priv *priv,
                skb_free_frag(skbh);
 
        /* Move on with skb release */
-       dev_kfree_skb(skb);
+       napi_consume_skb(skb, in_napi);
 }
 
 static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
@@ -785,9 +783,7 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
        queue_mapping = skb_get_queue_mapping(skb);
        fq = &priv->fq[queue_mapping];
        for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
-               err = dpaa2_io_service_enqueue_qd(fq->channel->dpio,
-                                                 priv->tx_qdid, 0,
-                                                 fq->tx_qdbin, &fd);
+               err = priv->enqueue(priv, fq, &fd, 0);
                if (err != -EBUSY)
                        break;
        }
@@ -795,7 +791,7 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
        if (unlikely(err < 0)) {
                percpu_stats->tx_errors++;
                /* Clean up everything, including freeing the skb */
-               free_tx_fd(priv, &fd);
+               free_tx_fd(priv, &fd, false);
        } else {
                fd_len = dpaa2_fd_get_len(&fd);
                percpu_stats->tx_packets++;
@@ -837,7 +833,7 @@ static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv,
 
        /* Check frame errors in the FD field */
        fd_errors = dpaa2_fd_get_ctrl(fd) & DPAA2_FD_TX_ERR_MASK;
-       free_tx_fd(priv, fd);
+       free_tx_fd(priv, fd, true);
 
        if (likely(!fd_errors))
                return;
@@ -903,7 +899,7 @@ static int add_bufs(struct dpaa2_eth_priv *priv,
 {
        struct device *dev = priv->net_dev->dev.parent;
        u64 buf_array[DPAA2_ETH_BUFS_PER_CMD];
-       void *buf;
+       struct page *page;
        dma_addr_t addr;
        int i, err;
 
@@ -911,14 +907,16 @@ static int add_bufs(struct dpaa2_eth_priv *priv,
                /* Allocate buffer visible to WRIOP + skb shared info +
                 * alignment padding
                 */
-               buf = napi_alloc_frag(dpaa2_eth_buf_raw_size(priv));
-               if (unlikely(!buf))
+               /* allocate one page for each Rx buffer. WRIOP sees
+                * the entire page except for a tailroom reserved for
+                * skb shared info
+                */
+               page = dev_alloc_pages(0);
+               if (!page)
                        goto err_alloc;
 
-               buf = PTR_ALIGN(buf, priv->rx_buf_align);
-
-               addr = dma_map_single(dev, buf, DPAA2_ETH_RX_BUF_SIZE,
-                                     DMA_BIDIRECTIONAL);
+               addr = dma_map_page(dev, page, 0, DPAA2_ETH_RX_BUF_SIZE,
+                                   DMA_BIDIRECTIONAL);
                if (unlikely(dma_mapping_error(dev, addr)))
                        goto err_map;
 
@@ -926,7 +924,7 @@ static int add_bufs(struct dpaa2_eth_priv *priv,
 
                /* tracing point */
                trace_dpaa2_eth_buf_seed(priv->net_dev,
-                                        buf, dpaa2_eth_buf_raw_size(priv),
+                                        page, DPAA2_ETH_RX_BUF_RAW_SIZE,
                                         addr, DPAA2_ETH_RX_BUF_SIZE,
                                         bpid);
        }
@@ -948,7 +946,7 @@ release_bufs:
        return i;
 
 err_map:
-       skb_free_frag(buf);
+       __free_pages(page, 0);
 err_alloc:
        /* If we managed to allocate at least some buffers,
         * release them to hardware
@@ -2134,6 +2132,7 @@ static int set_buffer_layout(struct dpaa2_eth_priv *priv)
 {
        struct device *dev = priv->net_dev->dev.parent;
        struct dpni_buffer_layout buf_layout = {0};
+       u16 rx_buf_align;
        int err;
 
        /* We need to check for WRIOP version 1.0.0, but depending on the MC
@@ -2142,9 +2141,9 @@ static int set_buffer_layout(struct dpaa2_eth_priv *priv)
         */
        if (priv->dpni_attrs.wriop_version == DPAA2_WRIOP_VERSION(0, 0, 0) ||
            priv->dpni_attrs.wriop_version == DPAA2_WRIOP_VERSION(1, 0, 0))
-               priv->rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN_REV1;
+               rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN_REV1;
        else
-               priv->rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN;
+               rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN;
 
        /* tx buffer */
        buf_layout.private_data_size = DPAA2_ETH_SWA_SIZE;
@@ -2184,7 +2183,7 @@ static int set_buffer_layout(struct dpaa2_eth_priv *priv)
        /* rx buffer */
        buf_layout.pass_frame_status = true;
        buf_layout.pass_parser_result = true;
-       buf_layout.data_align = priv->rx_buf_align;
+       buf_layout.data_align = rx_buf_align;
        buf_layout.data_head_room = dpaa2_eth_rx_head_room(priv);
        buf_layout.private_data_size = 0;
        buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT |
@@ -2202,6 +2201,36 @@ static int set_buffer_layout(struct dpaa2_eth_priv *priv)
        return 0;
 }
 
+#define DPNI_ENQUEUE_FQID_VER_MAJOR    7
+#define DPNI_ENQUEUE_FQID_VER_MINOR    9
+
+static inline int dpaa2_eth_enqueue_qd(struct dpaa2_eth_priv *priv,
+                                      struct dpaa2_eth_fq *fq,
+                                      struct dpaa2_fd *fd, u8 prio)
+{
+       return dpaa2_io_service_enqueue_qd(fq->channel->dpio,
+                                          priv->tx_qdid, prio,
+                                          fq->tx_qdbin, fd);
+}
+
+static inline int dpaa2_eth_enqueue_fq(struct dpaa2_eth_priv *priv,
+                                      struct dpaa2_eth_fq *fq,
+                                      struct dpaa2_fd *fd,
+                                      u8 prio __always_unused)
+{
+       return dpaa2_io_service_enqueue_fq(fq->channel->dpio,
+                                          fq->tx_fqid, fd);
+}
+
+static void set_enqueue_mode(struct dpaa2_eth_priv *priv)
+{
+       if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_ENQUEUE_FQID_VER_MAJOR,
+                                  DPNI_ENQUEUE_FQID_VER_MINOR) < 0)
+               priv->enqueue = dpaa2_eth_enqueue_qd;
+       else
+               priv->enqueue = dpaa2_eth_enqueue_fq;
+}
+
 /* Configure the DPNI object this interface is associated with */
 static int setup_dpni(struct fsl_mc_device *ls_dev)
 {
@@ -2255,6 +2284,8 @@ static int setup_dpni(struct fsl_mc_device *ls_dev)
        if (err)
                goto close;
 
+       set_enqueue_mode(priv);
+
        priv->cls_rules = devm_kzalloc(dev, sizeof(struct dpaa2_eth_cls_rule) *
                                       dpaa2_eth_fs_count(priv), GFP_KERNEL);
        if (!priv->cls_rules)
@@ -2339,6 +2370,7 @@ static int setup_tx_flow(struct dpaa2_eth_priv *priv,
        }
 
        fq->tx_qdbin = qid.qdbin;
+       fq->tx_fqid = qid.fqid;
 
        err = dpni_get_queue(priv->mc_io, 0, priv->mc_token,
                             DPNI_QUEUE_TX_CONFIRM, 0, fq->flowid,
index 31fe486..9510928 100644 (file)
@@ -53,7 +53,8 @@
  */
 #define DPAA2_ETH_MAX_FRAMES_PER_QUEUE (DPAA2_ETH_TAILDROP_THRESH / 64)
 #define DPAA2_ETH_NUM_BUFS             (DPAA2_ETH_MAX_FRAMES_PER_QUEUE + 256)
-#define DPAA2_ETH_REFILL_THRESH                DPAA2_ETH_MAX_FRAMES_PER_QUEUE
+#define DPAA2_ETH_REFILL_THRESH \
+       (DPAA2_ETH_NUM_BUFS - DPAA2_ETH_BUFS_PER_CMD)
 
 /* Maximum number of buffers that can be acquired/released through a single
  * QBMan command
 /* Hardware requires alignment for ingress/egress buffer addresses */
 #define DPAA2_ETH_TX_BUF_ALIGN         64
 
-#define DPAA2_ETH_RX_BUF_SIZE          2048
-#define DPAA2_ETH_SKB_SIZE \
-       (DPAA2_ETH_RX_BUF_SIZE + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+#define DPAA2_ETH_RX_BUF_RAW_SIZE      PAGE_SIZE
+#define DPAA2_ETH_RX_BUF_TAILROOM \
+       SKB_DATA_ALIGN(sizeof(struct skb_shared_info))
+#define DPAA2_ETH_RX_BUF_SIZE \
+       (DPAA2_ETH_RX_BUF_RAW_SIZE - DPAA2_ETH_RX_BUF_TAILROOM)
 
 /* Hardware annotation area in RX/TX buffers */
 #define DPAA2_ETH_RX_HWA_SIZE          64
@@ -274,6 +277,7 @@ struct dpaa2_eth_priv;
 struct dpaa2_eth_fq {
        u32 fqid;
        u32 tx_qdbin;
+       u32 tx_fqid;
        u16 flowid;
        int target_cpu;
        u32 dq_frames;
@@ -326,6 +330,9 @@ struct dpaa2_eth_priv {
 
        u8 num_fqs;
        struct dpaa2_eth_fq fq[DPAA2_ETH_MAX_QUEUES];
+       int (*enqueue)(struct dpaa2_eth_priv *priv,
+                      struct dpaa2_eth_fq *fq,
+                      struct dpaa2_fd *fd, u8 prio);
 
        u8 num_channels;
        struct dpaa2_eth_channel *channel[DPAA2_ETH_MAX_DPCONS];
@@ -343,7 +350,6 @@ struct dpaa2_eth_priv {
        bool rx_tstamp; /* Rx timestamping enabled */
 
        u16 tx_qdid;
-       u16 rx_buf_align;
        struct fsl_mc_io *mc_io;
        /* Cores which have an affine DPIO/DPCON.
         * This is the cpu set on which Rx and Tx conf frames are processed
@@ -418,15 +424,6 @@ enum dpaa2_eth_rx_dist {
        DPAA2_ETH_RX_DIST_CLS
 };
 
-/* Hardware only sees DPAA2_ETH_RX_BUF_SIZE, but the skb built around
- * the buffer also needs space for its shared info struct, and we need
- * to allocate enough to accommodate hardware alignment restrictions
- */
-static inline unsigned int dpaa2_eth_buf_raw_size(struct dpaa2_eth_priv *priv)
-{
-       return DPAA2_ETH_SKB_SIZE + priv->rx_buf_align;
-}
-
 static inline
 unsigned int dpaa2_eth_needed_headroom(struct dpaa2_eth_priv *priv,
                                       struct sk_buff *skb)
@@ -451,8 +448,7 @@ unsigned int dpaa2_eth_needed_headroom(struct dpaa2_eth_priv *priv,
  */
 static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv)
 {
-       return priv->tx_data_offset + DPAA2_ETH_TX_BUF_ALIGN -
-              DPAA2_ETH_RX_HWA_SIZE;
+       return priv->tx_data_offset - DPAA2_ETH_RX_HWA_SIZE;
 }
 
 int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags);
index f9dd26f..8429f5c 100644 (file)
@@ -17,3 +17,15 @@ config FSL_ENETC_VF
          virtual function (VF) devices enabled by the ENETC PF driver.
 
          If compiled as module (M), the module name is fsl-enetc-vf.
+
+config FSL_ENETC_PTP_CLOCK
+       tristate "ENETC PTP clock driver"
+       depends on PTP_1588_CLOCK_QORIQ && (FSL_ENETC || FSL_ENETC_VF)
+       default y
+       help
+         This driver adds support for using the ENETC 1588 timer
+         as a PTP clock. This clock is only useful if your PTP
+         programs are getting hardware time stamps on the PTP Ethernet
+         packets using the SO_TIMESTAMPING API.
+
+         If compiled as module (M), the module name is fsl-enetc-ptp.
index 9529b01..6976602 100644 (file)
@@ -13,3 +13,6 @@ fsl-enetc-vf-$(CONFIG_FSL_ENETC_VF) += enetc.o enetc_cbdr.o \
                                       enetc_ethtool.o
 fsl-enetc-vf-objs := enetc_vf.o $(fsl-enetc-vf-y)
 endif
+
+obj-$(CONFIG_FSL_ENETC_PTP_CLOCK) += fsl-enetc-ptp.o
+fsl-enetc-ptp-$(CONFIG_FSL_ENETC_PTP_CLOCK) += enetc_ptp.o
index efa0b1a..df8eb88 100644 (file)
@@ -4,8 +4,9 @@
 #include <linux/bitops.h>
 
 /* ENETC device IDs */
-#define ENETC_DEV_ID_PF        0xe100
-#define ENETC_DEV_ID_VF        0xef00
+#define ENETC_DEV_ID_PF                0xe100
+#define ENETC_DEV_ID_VF                0xef00
+#define ENETC_DEV_ID_PTP       0xee02
 
 /* ENETC register block BAR */
 #define ENETC_BAR_REGS 0
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ptp.c b/drivers/net/ethernet/freescale/enetc/enetc_ptp.c
new file mode 100644 (file)
index 0000000..dc2f58a
--- /dev/null
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* Copyright 2019 NXP */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/fsl/ptp_qoriq.h>
+
+#include "enetc.h"
+
+static struct ptp_clock_info enetc_ptp_caps = {
+       .owner          = THIS_MODULE,
+       .name           = "ENETC PTP clock",
+       .max_adj        = 512000,
+       .n_alarm        = 0,
+       .n_ext_ts       = 2,
+       .n_per_out      = 0,
+       .n_pins         = 0,
+       .pps            = 1,
+       .adjfine        = ptp_qoriq_adjfine,
+       .adjtime        = ptp_qoriq_adjtime,
+       .gettime64      = ptp_qoriq_gettime,
+       .settime64      = ptp_qoriq_settime,
+       .enable         = ptp_qoriq_enable,
+};
+
+static int enetc_ptp_probe(struct pci_dev *pdev,
+                          const struct pci_device_id *ent)
+{
+       struct ptp_qoriq *ptp_qoriq;
+       void __iomem *base;
+       int err, len, n;
+
+       if (pdev->dev.of_node && !of_device_is_available(pdev->dev.of_node)) {
+               dev_info(&pdev->dev, "device is disabled, skipping\n");
+               return -ENODEV;
+       }
+
+       err = pci_enable_device_mem(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "device enable failed\n");
+               return err;
+       }
+
+       /* 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;
+               }
+       }
+
+       err = pci_request_mem_regions(pdev, KBUILD_MODNAME);
+       if (err) {
+               dev_err(&pdev->dev, "pci_request_regions failed err=%d\n", err);
+               goto err_pci_mem_reg;
+       }
+
+       pci_set_master(pdev);
+
+       ptp_qoriq = kzalloc(sizeof(*ptp_qoriq), GFP_KERNEL);
+       if (!ptp_qoriq) {
+               err = -ENOMEM;
+               goto err_alloc_ptp;
+       }
+
+       len = pci_resource_len(pdev, ENETC_BAR_REGS);
+
+       base = ioremap(pci_resource_start(pdev, ENETC_BAR_REGS), len);
+       if (!base) {
+               err = -ENXIO;
+               dev_err(&pdev->dev, "ioremap() failed\n");
+               goto err_ioremap;
+       }
+
+       /* Allocate 1 interrupt */
+       n = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
+       if (n != 1) {
+               err = -EPERM;
+               goto err_irq;
+       }
+
+       ptp_qoriq->irq = pci_irq_vector(pdev, 0);
+
+       err = request_irq(ptp_qoriq->irq, ptp_qoriq_isr, 0, DRIVER, ptp_qoriq);
+       if (err) {
+               dev_err(&pdev->dev, "request_irq() failed!\n");
+               goto err_irq;
+       }
+
+       ptp_qoriq->dev = &pdev->dev;
+
+       err = ptp_qoriq_init(ptp_qoriq, base, enetc_ptp_caps);
+       if (err)
+               goto err_no_clock;
+
+       pci_set_drvdata(pdev, ptp_qoriq);
+
+       return 0;
+
+err_no_clock:
+       free_irq(ptp_qoriq->irq, ptp_qoriq);
+err_irq:
+       iounmap(base);
+err_ioremap:
+       kfree(ptp_qoriq);
+err_alloc_ptp:
+       pci_release_mem_regions(pdev);
+err_pci_mem_reg:
+err_dma:
+       pci_disable_device(pdev);
+
+       return err;
+}
+
+static void enetc_ptp_remove(struct pci_dev *pdev)
+{
+       struct ptp_qoriq *ptp_qoriq = pci_get_drvdata(pdev);
+
+       ptp_qoriq_free(ptp_qoriq);
+       kfree(ptp_qoriq);
+
+       pci_release_mem_regions(pdev);
+       pci_disable_device(pdev);
+}
+
+static const struct pci_device_id enetc_ptp_id_table[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, ENETC_DEV_ID_PTP) },
+       { 0, } /* End of table. */
+};
+MODULE_DEVICE_TABLE(pci, enetc_ptp_id_table);
+
+static struct pci_driver enetc_ptp_driver = {
+       .name = KBUILD_MODNAME,
+       .id_table = enetc_ptp_id_table,
+       .probe = enetc_ptp_probe,
+       .remove = enetc_ptp_remove,
+};
+module_pci_driver(enetc_ptp_driver);
+
+MODULE_DESCRIPTION("ENETC PTP clock driver");
+MODULE_LICENSE("Dual BSD/GPL");
index 2370dc2..697c242 100644 (file)
@@ -2098,6 +2098,7 @@ static int fec_enet_get_regs_len(struct net_device *ndev)
 #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
        defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
        defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST)
+static __u32 fec_enet_register_version = 2;
 static u32 fec_enet_register_offset[] = {
        FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0,
        FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL,
@@ -2128,6 +2129,7 @@ static u32 fec_enet_register_offset[] = {
        IEEE_R_FDXFC, IEEE_R_OCTETS_OK
 };
 #else
+static __u32 fec_enet_register_version = 1;
 static u32 fec_enet_register_offset[] = {
        FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0,
        FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0,
@@ -2149,6 +2151,8 @@ static void fec_enet_get_regs(struct net_device *ndev,
        u32 *buf = (u32 *)regbuf;
        u32 i, off;
 
+       regs->version = fec_enet_register_version;
+
        memset(buf, 0, regs->len);
 
        for (i = 0; i < ARRAY_SIZE(fec_enet_register_offset); i++) {
index b90bab7..c1968b3 100644 (file)
@@ -369,7 +369,7 @@ static irqreturn_t mpc52xx_fec_tx_interrupt(int irq, void *dev_id)
                dma_unmap_single(dev->dev.parent, bd->skb_pa, skb->len,
                                 DMA_TO_DEVICE);
 
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
        }
        spin_unlock(&priv->lock);
 
index 241325c..27ed995 100644 (file)
@@ -1492,7 +1492,7 @@ static int gfar_get_ts_info(struct net_device *dev,
        struct gfar_private *priv = netdev_priv(dev);
        struct platform_device *ptp_dev;
        struct device_node *ptp_node;
-       struct qoriq_ptp *ptp = NULL;
+       struct ptp_qoriq *ptp = NULL;
 
        info->phc_index = -1;
 
index c3d539e..eb3e65e 100644 (file)
@@ -1879,6 +1879,8 @@ static void ucc_geth_free_tx(struct ucc_geth_private *ugeth)
        u16 i, j;
        u8 __iomem *bd;
 
+       netdev_reset_queue(ugeth->ndev);
+
        ug_info = ugeth->ug_info;
        uf_info = &ug_info->uf_info;
 
index 3b9e74b..b8155f5 100644 (file)
@@ -3081,6 +3081,7 @@ int hns_dsaf_roce_reset(struct fwnode_handle *dsaf_fwnode, bool dereset)
        dsaf_dev = dev_get_drvdata(&pdev->dev);
        if (!dsaf_dev) {
                dev_err(&pdev->dev, "dsaf_dev is NULL\n");
+               put_device(&pdev->dev);
                return -ENODEV;
        }
 
@@ -3088,6 +3089,7 @@ int hns_dsaf_roce_reset(struct fwnode_handle *dsaf_fwnode, bool dereset)
        if (AE_IS_VER1(dsaf_dev->dsaf_ver)) {
                dev_err(dsaf_dev->dev, "%s v1 chip doesn't support RoCE!\n",
                        dsaf_dev->ae_dev.name);
+               put_device(&pdev->dev);
                return -ENODEV;
        }
 
index 5b33238..60e7d7a 100644 (file)
@@ -2418,6 +2418,8 @@ static int hns_nic_dev_probe(struct platform_device *pdev)
 out_notify_fail:
        (void)cancel_work_sync(&priv->service_task);
 out_read_prop_fail:
+       /* safe for ACPI FW */
+       of_node_put(to_of_node(priv->fwnode));
        free_netdev(ndev);
        return ret;
 }
@@ -2447,6 +2449,9 @@ static int hns_nic_dev_remove(struct platform_device *pdev)
        set_bit(NIC_STATE_REMOVING, &priv->state);
        (void)cancel_work_sync(&priv->service_task);
 
+       /* safe for ACPI FW */
+       of_node_put(to_of_node(priv->fwnode));
+
        free_netdev(ndev);
        return 0;
 }
index 8e9b958..ce15d23 100644 (file)
@@ -1157,16 +1157,18 @@ static int hns_get_regs_len(struct net_device *net_dev)
  */
 static int hns_nic_nway_reset(struct net_device *netdev)
 {
-       int ret = 0;
        struct phy_device *phy = netdev->phydev;
 
-       if (netif_running(netdev)) {
-               /* if autoneg is disabled, don't restart auto-negotiation */
-               if (phy && phy->autoneg == AUTONEG_ENABLE)
-                       ret = genphy_restart_aneg(phy);
-       }
+       if (!netif_running(netdev))
+               return 0;
 
-       return ret;
+       if (!phy)
+               return -EOPNOTSUPP;
+
+       if (phy->autoneg != AUTONEG_ENABLE)
+               return -EINVAL;
+
+       return genphy_restart_aneg(phy);
 }
 
 static u32
index 691d121..53089cd 100644 (file)
@@ -40,6 +40,8 @@ enum HCLGE_MBX_OPCODE {
        HCLGE_MBX_SET_ALIVE,            /* (VF -> PF) set alive state */
        HCLGE_MBX_SET_MTU,              /* (VF -> PF) set mtu */
        HCLGE_MBX_GET_QID_IN_PF,        /* (VF -> PF) get queue id in pf */
+       HCLGE_MBX_LINK_STAT_MODE,       /* (PF -> VF) link mode has changed */
+       HCLGE_MBX_GET_LINK_MODE,        /* (VF -> PF) get the link mode of pf */
 };
 
 /* below are per-VF mac-vlan subcodes */
@@ -60,7 +62,7 @@ enum hclge_mbx_vlan_cfg_subcode {
 };
 
 #define HCLGE_MBX_MAX_MSG_SIZE 16
-#define HCLGE_MBX_MAX_RESP_DATA_SIZE   8
+#define HCLGE_MBX_MAX_RESP_DATA_SIZE   16
 #define HCLGE_MBX_RING_MAP_BASIC_MSG_NUM       3
 #define HCLGE_MBX_RING_NODE_VARIABLE_NUM       3
 
index 781e5de..50011aa 100644 (file)
@@ -238,7 +238,7 @@ EXPORT_SYMBOL(hnae3_unregister_ae_algo);
  * @ae_dev: the AE device
  * NOTE: the duplicated name will not be checked
  */
-void hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
+int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
 {
        const struct pci_device_id *id;
        struct hnae3_ae_algo *ae_algo;
@@ -259,6 +259,7 @@ void hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
 
                if (!ae_dev->ops) {
                        dev_err(&ae_dev->pdev->dev, "ae_dev ops are null\n");
+                       ret = -EOPNOTSUPP;
                        goto out_err;
                }
 
@@ -285,8 +286,15 @@ void hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
                                ret);
        }
 
+       mutex_unlock(&hnae3_common_lock);
+
+       return 0;
+
 out_err:
+       list_del(&ae_dev->node);
        mutex_unlock(&hnae3_common_lock);
+
+       return ret;
 }
 EXPORT_SYMBOL(hnae3_register_ae_dev);
 
index 585800e..10f1cc2 100644 (file)
@@ -461,9 +461,11 @@ struct hnae3_ae_ops {
        bool (*get_hw_reset_stat)(struct hnae3_handle *handle);
        bool (*ae_dev_resetting)(struct hnae3_handle *handle);
        unsigned long (*ae_dev_reset_cnt)(struct hnae3_handle *handle);
-       int (*set_gro_en)(struct hnae3_handle *handle, int enable);
+       int (*set_gro_en)(struct hnae3_handle *handle, bool enable);
        u16 (*get_global_queue_id)(struct hnae3_handle *handle, u16 queue_id);
        void (*set_timer_task)(struct hnae3_handle *handle, bool enable);
+       int (*mac_connect_phy)(struct hnae3_handle *handle);
+       void (*mac_disconnect_phy)(struct hnae3_handle *handle);
 };
 
 struct hnae3_dcb_ops {
@@ -587,7 +589,7 @@ struct hnae3_handle {
 #define hnae3_get_bit(origin, shift) \
        hnae3_get_field((origin), (0x1 << (shift)), (shift))
 
-void hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev);
+int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev);
 void hnae3_unregister_ae_dev(struct hnae3_ae_dev *ae_dev);
 
 void hnae3_unregister_ae_algo(struct hnae3_ae_algo *ae_algo);
index 4b38c37..40b1722 100644 (file)
@@ -655,11 +655,7 @@ static int hns3_set_tso(struct sk_buff *skb, u32 *paylen,
 static int hns3_get_l4_protocol(struct sk_buff *skb, u8 *ol4_proto,
                                u8 *il4_proto)
 {
-       union {
-               struct iphdr *v4;
-               struct ipv6hdr *v6;
-               unsigned char *hdr;
-       } l3;
+       union l3_hdr_info l3;
        unsigned char *l4_hdr;
        unsigned char *exthdr;
        u8 l4_proto_tmp;
@@ -712,17 +708,8 @@ static void hns3_set_l2l3l4_len(struct sk_buff *skb, u8 ol4_proto,
                                u8 il4_proto, u32 *type_cs_vlan_tso,
                                u32 *ol_type_vlan_len_msec)
 {
-       union {
-               struct iphdr *v4;
-               struct ipv6hdr *v6;
-               unsigned char *hdr;
-       } l3;
-       union {
-               struct tcphdr *tcp;
-               struct udphdr *udp;
-               struct gre_base_hdr *gre;
-               unsigned char *hdr;
-       } l4;
+       union l3_hdr_info l3;
+       union l4_hdr_info l4;
        unsigned char *l2_hdr;
        u8 l4_proto = ol4_proto;
        u32 ol2_len;
@@ -821,12 +808,7 @@ static void hns3_set_l2l3l4_len(struct sk_buff *skb, u8 ol4_proto,
 static bool hns3_tunnel_csum_bug(struct sk_buff *skb)
 {
 #define IANA_VXLAN_PORT        4789
-       union {
-               struct tcphdr *tcp;
-               struct udphdr *udp;
-               struct gre_base_hdr *gre;
-               unsigned char *hdr;
-       } l4;
+       union l4_hdr_info l4;
 
        l4.hdr = skb_transport_header(skb);
 
@@ -842,11 +824,7 @@ static int hns3_set_l3l4_type_csum(struct sk_buff *skb, u8 ol4_proto,
                                   u8 il4_proto, u32 *type_cs_vlan_tso,
                                   u32 *ol_type_vlan_len_msec)
 {
-       union {
-               struct iphdr *v4;
-               struct ipv6hdr *v6;
-               unsigned char *hdr;
-       } l3;
+       union l3_hdr_info l3;
        u32 l4_proto = ol4_proto;
 
        l3.hdr = skb_network_header(skb);
@@ -1371,6 +1349,7 @@ static int hns3_nic_set_features(struct net_device *netdev,
        netdev_features_t changed = netdev->features ^ features;
        struct hns3_nic_priv *priv = netdev_priv(netdev);
        struct hnae3_handle *h = priv->ae_handle;
+       bool enable;
        int ret;
 
        if (changed & (NETIF_F_TSO | NETIF_F_TSO6)) {
@@ -1381,38 +1360,29 @@ static int hns3_nic_set_features(struct net_device *netdev,
        }
 
        if (changed & (NETIF_F_GRO_HW) && h->ae_algo->ops->set_gro_en) {
-               if (features & NETIF_F_GRO_HW)
-                       ret = h->ae_algo->ops->set_gro_en(h, true);
-               else
-                       ret = h->ae_algo->ops->set_gro_en(h, false);
+               enable = !!(features & NETIF_F_GRO_HW);
+               ret = h->ae_algo->ops->set_gro_en(h, enable);
                if (ret)
                        return ret;
        }
 
        if ((changed & NETIF_F_HW_VLAN_CTAG_FILTER) &&
            h->ae_algo->ops->enable_vlan_filter) {
-               if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
-                       h->ae_algo->ops->enable_vlan_filter(h, true);
-               else
-                       h->ae_algo->ops->enable_vlan_filter(h, false);
+               enable = !!(features & NETIF_F_HW_VLAN_CTAG_FILTER);
+               h->ae_algo->ops->enable_vlan_filter(h, enable);
        }
 
        if ((changed & NETIF_F_HW_VLAN_CTAG_RX) &&
            h->ae_algo->ops->enable_hw_strip_rxvtag) {
-               if (features & NETIF_F_HW_VLAN_CTAG_RX)
-                       ret = h->ae_algo->ops->enable_hw_strip_rxvtag(h, true);
-               else
-                       ret = h->ae_algo->ops->enable_hw_strip_rxvtag(h, false);
-
+               enable = !!(features & NETIF_F_HW_VLAN_CTAG_RX);
+               ret = h->ae_algo->ops->enable_hw_strip_rxvtag(h, enable);
                if (ret)
                        return ret;
        }
 
        if ((changed & NETIF_F_NTUPLE) && h->ae_algo->ops->enable_fd) {
-               if (features & NETIF_F_NTUPLE)
-                       h->ae_algo->ops->enable_fd(h, true);
-               else
-                       h->ae_algo->ops->enable_fd(h, false);
+               enable = !!(features & NETIF_F_NTUPLE);
+               h->ae_algo->ops->enable_fd(h, enable);
        }
 
        netdev->features = features;
@@ -1774,9 +1744,13 @@ static int hns3_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        hns3_get_dev_capability(pdev, ae_dev);
        pci_set_drvdata(pdev, ae_dev);
 
-       hnae3_register_ae_dev(ae_dev);
+       ret = hnae3_register_ae_dev(ae_dev);
+       if (ret) {
+               devm_kfree(&pdev->dev, ae_dev);
+               pci_set_drvdata(pdev, NULL);
+       }
 
-       return 0;
+       return ret;
 }
 
 /* hns3_remove - Device removal routine
@@ -1955,8 +1929,7 @@ static void hns3_set_default_feature(struct net_device *netdev)
                NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_SCTP_CRC;
 
        if (pdev->revision >= 0x21) {
-               netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER |
-                       NETIF_F_GRO_HW;
+               netdev->hw_features |= NETIF_F_GRO_HW;
                netdev->features |= NETIF_F_GRO_HW;
 
                if (!(h->flags & HNAE3_SUPPORT_VF)) {
@@ -2795,7 +2768,7 @@ static bool hns3_get_new_int_gl(struct hns3_enet_ring_group *ring_group)
        u32 time_passed_ms;
        u16 new_int_gl;
 
-       if (!ring_group->coal.int_gl || !tqp_vector->last_jiffies)
+       if (!tqp_vector->last_jiffies)
                return false;
 
        if (ring_group->total_packets == 0) {
@@ -2898,7 +2871,7 @@ static void hns3_update_new_int_gl(struct hns3_enet_tqp_vector *tqp_vector)
        }
 
        if (tx_group->coal.gl_adapt_enable) {
-               tx_update = hns3_get_new_int_gl(&tqp_vector->tx_group);
+               tx_update = hns3_get_new_int_gl(tx_group);
                if (tx_update)
                        hns3_set_vector_coalesce_tx_gl(tqp_vector,
                                                       tx_group->coal.int_gl);
@@ -3201,12 +3174,12 @@ static void hns3_clear_ring_group(struct hns3_enet_ring_group *group)
        group->count = 0;
 }
 
-static int hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv)
+static void hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv)
 {
        struct hnae3_ring_chain_node vector_ring_chain;
        struct hnae3_handle *h = priv->ae_handle;
        struct hns3_enet_tqp_vector *tqp_vector;
-       int i, ret;
+       int i;
 
        for (i = 0; i < priv->vector_num; i++) {
                tqp_vector = &priv->tqp_vector[i];
@@ -3214,15 +3187,10 @@ static int hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv)
                if (!tqp_vector->rx_group.ring && !tqp_vector->tx_group.ring)
                        continue;
 
-               ret = hns3_get_vector_ring_chain(tqp_vector,
-                                                &vector_ring_chain);
-               if (ret)
-                       return ret;
+               hns3_get_vector_ring_chain(tqp_vector, &vector_ring_chain);
 
-               ret = h->ae_algo->ops->unmap_ring_from_vector(h,
+               h->ae_algo->ops->unmap_ring_from_vector(h,
                        tqp_vector->vector_irq, &vector_ring_chain);
-               if (ret)
-                       return ret;
 
                hns3_free_vector_ring_chain(tqp_vector, &vector_ring_chain);
 
@@ -3238,8 +3206,6 @@ static int hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv)
                hns3_clear_ring_group(&tqp_vector->tx_group);
                netif_napi_del(&priv->tqp_vector[i].napi);
        }
-
-       return 0;
 }
 
 static int hns3_nic_dealloc_vector_data(struct hns3_nic_priv *priv)
@@ -3549,6 +3515,25 @@ static int hns3_init_mac_addr(struct net_device *netdev, bool init)
        return ret;
 }
 
+static int hns3_init_phy(struct net_device *netdev)
+{
+       struct hnae3_handle *h = hns3_get_handle(netdev);
+       int ret = 0;
+
+       if (h->ae_algo->ops->mac_connect_phy)
+               ret = h->ae_algo->ops->mac_connect_phy(h);
+
+       return ret;
+}
+
+static void hns3_uninit_phy(struct net_device *netdev)
+{
+       struct hnae3_handle *h = hns3_get_handle(netdev);
+
+       if (h->ae_algo->ops->mac_disconnect_phy)
+               h->ae_algo->ops->mac_disconnect_phy(h);
+}
+
 static int hns3_restore_fd_rules(struct net_device *netdev)
 {
        struct hnae3_handle *h = hns3_get_handle(netdev);
@@ -3658,6 +3643,10 @@ static int hns3_client_init(struct hnae3_handle *handle)
                goto out_init_ring_data;
        }
 
+       ret = hns3_init_phy(netdev);
+       if (ret)
+               goto out_init_phy;
+
        ret = register_netdev(netdev);
        if (ret) {
                dev_err(priv->dev, "probe register netdev fail!\n");
@@ -3682,8 +3671,11 @@ static int hns3_client_init(struct hnae3_handle *handle)
        return ret;
 
 out_reg_netdev_fail:
+       hns3_uninit_phy(netdev);
+out_init_phy:
+       hns3_uninit_all_ring(priv);
 out_init_ring_data:
-       (void)hns3_nic_uninit_vector_data(priv);
+       hns3_nic_uninit_vector_data(priv);
 out_init_vector_data:
        hns3_nic_dealloc_vector_data(priv);
 out_alloc_vector_data:
@@ -3716,9 +3708,9 @@ static void hns3_client_uninit(struct hnae3_handle *handle, bool reset)
 
        hns3_force_clear_all_rx_ring(handle);
 
-       ret = hns3_nic_uninit_vector_data(priv);
-       if (ret)
-               netdev_err(netdev, "uninit vector error\n");
+       hns3_uninit_phy(netdev);
+
+       hns3_nic_uninit_vector_data(priv);
 
        ret = hns3_nic_dealloc_vector_data(priv);
        if (ret)
@@ -4111,11 +4103,7 @@ static int hns3_reset_notify_uninit_enet(struct hnae3_handle *handle)
 
        hns3_force_clear_all_rx_ring(handle);
 
-       ret = hns3_nic_uninit_vector_data(priv);
-       if (ret) {
-               netdev_err(netdev, "uninit vector error\n");
-               return ret;
-       }
+       hns3_nic_uninit_vector_data(priv);
 
        hns3_store_coal(priv);
 
index f3d2486..71ff8f4 100644 (file)
@@ -574,6 +574,7 @@ union l3_hdr_info {
 union l4_hdr_info {
        struct tcphdr *tcp;
        struct udphdr *udp;
+       struct gre_base_hdr *gre;
        unsigned char *hdr;
 };
 
index ed73f7f..63f5f56 100644 (file)
@@ -805,7 +805,7 @@ static int hns3_set_ringparam(struct net_device *ndev,
                    old_desc_num, new_desc_num);
 
        if (if_running)
-               dev_close(ndev);
+               ndev->netdev_ops->ndo_stop(ndev);
 
        ret = hns3_uninit_all_ring(priv);
        if (ret)
@@ -822,7 +822,7 @@ static int hns3_set_ringparam(struct net_device *ndev,
        }
 
        if (if_running)
-               ret = dev_open(ndev, NULL);
+               ret = ndev->netdev_ops->ndo_open(ndev);
 
        return ret;
 }
@@ -1115,6 +1115,8 @@ static const struct ethtool_ops hns3vf_ethtool_ops = {
        .get_channels = hns3_get_channels,
        .get_coalesce = hns3_get_coalesce,
        .set_coalesce = hns3_set_coalesce,
+       .get_regs_len = hns3_get_regs_len,
+       .get_regs = hns3_get_regs,
        .get_link = hns3_get_link,
 };
 
index 81dbe1b..1dddfcb 100644 (file)
@@ -186,6 +186,38 @@ static bool hclge_is_special_opcode(u16 opcode)
        return false;
 }
 
+static int hclge_cmd_check_retval(struct hclge_hw *hw, struct hclge_desc *desc,
+                                 int num, int ntc)
+{
+       u16 opcode, desc_ret;
+       int handle;
+       int retval;
+
+       opcode = le16_to_cpu(desc[0].opcode);
+       for (handle = 0; handle < num; handle++) {
+               desc[handle] = hw->cmq.csq.desc[ntc];
+               ntc++;
+               if (ntc >= hw->cmq.csq.desc_num)
+                       ntc = 0;
+       }
+       if (likely(!hclge_is_special_opcode(opcode)))
+               desc_ret = le16_to_cpu(desc[num - 1].retval);
+       else
+               desc_ret = le16_to_cpu(desc[0].retval);
+
+       if (desc_ret == HCLGE_CMD_EXEC_SUCCESS)
+               retval = 0;
+       else if (desc_ret == HCLGE_CMD_NO_AUTH)
+               retval = -EPERM;
+       else if (desc_ret == HCLGE_CMD_NOT_SUPPORTED)
+               retval = -EOPNOTSUPP;
+       else
+               retval = -EIO;
+       hw->cmq.last_status = desc_ret;
+
+       return retval;
+}
+
 /**
  * hclge_cmd_send - send command to command queue
  * @hw: pointer to the hw struct
@@ -203,7 +235,6 @@ int hclge_cmd_send(struct hclge_hw *hw, struct hclge_desc *desc, int num)
        u32 timeout = 0;
        int handle = 0;
        int retval = 0;
-       u16 opcode, desc_ret;
        int ntc;
 
        spin_lock_bh(&hw->cmq.csq.lock);
@@ -219,12 +250,11 @@ int hclge_cmd_send(struct hclge_hw *hw, struct hclge_desc *desc, int num)
         * which will be use for hardware to write back
         */
        ntc = hw->cmq.csq.next_to_use;
-       opcode = le16_to_cpu(desc[0].opcode);
        while (handle < num) {
                desc_to_use = &hw->cmq.csq.desc[hw->cmq.csq.next_to_use];
                *desc_to_use = desc[handle];
                (hw->cmq.csq.next_to_use)++;
-               if (hw->cmq.csq.next_to_use == hw->cmq.csq.desc_num)
+               if (hw->cmq.csq.next_to_use >= hw->cmq.csq.desc_num)
                        hw->cmq.csq.next_to_use = 0;
                handle++;
        }
@@ -250,31 +280,7 @@ int hclge_cmd_send(struct hclge_hw *hw, struct hclge_desc *desc, int num)
        if (!complete) {
                retval = -EAGAIN;
        } else {
-               handle = 0;
-               while (handle < num) {
-                       /* Get the result of hardware write back */
-                       desc_to_use = &hw->cmq.csq.desc[ntc];
-                       desc[handle] = *desc_to_use;
-
-                       if (likely(!hclge_is_special_opcode(opcode)))
-                               desc_ret = le16_to_cpu(desc[handle].retval);
-                       else
-                               desc_ret = le16_to_cpu(desc[0].retval);
-
-                       if (desc_ret == HCLGE_CMD_EXEC_SUCCESS)
-                               retval = 0;
-                       else if (desc_ret == HCLGE_CMD_NO_AUTH)
-                               retval = -EPERM;
-                       else if (desc_ret == HCLGE_CMD_NOT_SUPPORTED)
-                               retval = -EOPNOTSUPP;
-                       else
-                               retval = -EIO;
-                       hw->cmq.last_status = desc_ret;
-                       ntc++;
-                       handle++;
-                       if (ntc == hw->cmq.csq.desc_num)
-                               ntc = 0;
-               }
+               retval = hclge_cmd_check_retval(hw, desc, num, ntc);
        }
 
        /* Clean the command send queue */
index e26a251..ac2cd21 100644 (file)
@@ -312,16 +312,16 @@ struct hclge_ctrl_vector_chain_cmd {
        u8 rsv;
 };
 
-#define HCLGE_TC_NUM           8
+#define HCLGE_MAX_TC_NUM               8
 #define HCLGE_TC0_PRI_BUF_EN_B 15 /* Bit 15 indicate enable or not */
 #define HCLGE_BUF_UNIT_S       7  /* Buf size is united by 128 bytes */
 struct hclge_tx_buff_alloc_cmd {
-       __le16 tx_pkt_buff[HCLGE_TC_NUM];
+       __le16 tx_pkt_buff[HCLGE_MAX_TC_NUM];
        u8 tx_buff_rsv[8];
 };
 
 struct hclge_rx_priv_buff_cmd {
-       __le16 buf_num[HCLGE_TC_NUM];
+       __le16 buf_num[HCLGE_MAX_TC_NUM];
        __le16 shared_buf;
        u8 rsv[6];
 };
@@ -367,7 +367,6 @@ struct hclge_priv_buf {
        u32 enable;     /* Enable TC private buffer or not */
 };
 
-#define HCLGE_MAX_TC_NUM       8
 struct hclge_shared_buf {
        struct hclge_waterline self;
        struct hclge_tc_thrd tc_thrd[HCLGE_MAX_TC_NUM];
index 961aedb..1161361 100644 (file)
@@ -93,13 +93,11 @@ static int hclge_dcb_common_validate(struct hclge_dev *hdev, u8 num_tc,
                }
        }
 
-       for (i = 0; i < hdev->num_alloc_vport; i++) {
-               if (num_tc > hdev->vport[i].alloc_tqps) {
-                       dev_err(&hdev->pdev->dev,
-                               "allocated tqp(%u) checking failed, %u > tqp(%u)\n",
-                               i, num_tc, hdev->vport[i].alloc_tqps);
-                       return -EINVAL;
-               }
+       if (num_tc > hdev->vport[0].alloc_tqps) {
+               dev_err(&hdev->pdev->dev,
+                       "allocated tqp checking failed, %u > tqp(%u)\n",
+                       num_tc, hdev->vport[0].alloc_tqps);
+               return -EINVAL;
        }
 
        return 0;
index 26d8050..1192cf6 100644 (file)
@@ -691,7 +691,7 @@ static void hclge_dbg_dump_qos_buf_cfg(struct hclge_dev *hdev)
        dev_info(&hdev->pdev->dev, "dump qos buf cfg\n");
 
        tx_buf_cmd = (struct hclge_tx_buff_alloc_cmd *)desc[0].data;
-       for (i = 0; i < HCLGE_TC_NUM; i++)
+       for (i = 0; i < HCLGE_MAX_TC_NUM; i++)
                dev_info(&hdev->pdev->dev, "tx_packet_buf_tc_%d: 0x%x\n", i,
                         tx_buf_cmd->tx_pkt_buff[i]);
 
@@ -703,7 +703,7 @@ static void hclge_dbg_dump_qos_buf_cfg(struct hclge_dev *hdev)
 
        dev_info(&hdev->pdev->dev, "\n");
        rx_buf_cmd = (struct hclge_rx_priv_buff_cmd *)desc[0].data;
-       for (i = 0; i < HCLGE_TC_NUM; i++)
+       for (i = 0; i < HCLGE_MAX_TC_NUM; i++)
                dev_info(&hdev->pdev->dev, "rx_packet_buf_tc_%d: 0x%x\n", i,
                         rx_buf_cmd->buf_num[i]);
 
index ae8336c..75ec309 100644 (file)
@@ -295,6 +295,14 @@ static const struct hclge_mac_mgr_tbl_entry_cmd hclge_mgr_table[] = {
        },
 };
 
+static const u8 hclge_hash_key[] = {
+       0x6D, 0x5A, 0x56, 0xDA, 0x25, 0x5B, 0x0E, 0xC2,
+       0x41, 0x67, 0x25, 0x3D, 0x43, 0xA3, 0x8F, 0xB0,
+       0xD0, 0xCA, 0x2B, 0xCB, 0xAE, 0x7B, 0x30, 0xB4,
+       0x77, 0xCB, 0x2D, 0xA3, 0x80, 0x30, 0xF2, 0x0C,
+       0x6A, 0x42, 0xB7, 0x3B, 0xBE, 0xAC, 0x01, 0xFA
+};
+
 static int hclge_mac_update_stats_defective(struct hclge_dev *hdev)
 {
 #define HCLGE_MAC_CMD_NUM 21
@@ -343,6 +351,8 @@ static int hclge_mac_update_stats_complete(struct hclge_dev *hdev, u32 desc_num)
        int ret;
 
        desc = kcalloc(desc_num, sizeof(struct hclge_desc), GFP_KERNEL);
+       if (!desc)
+               return -ENOMEM;
        hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_STATS_MAC_ALL, true);
        ret = hclge_cmd_send(&hdev->hw, desc, desc_num);
        if (ret) {
@@ -999,6 +1009,9 @@ static int hclge_configure(struct hclge_dev *hdev)
        hdev->tm_info.hw_pfc_map = 0;
        hdev->wanted_umv_size = cfg.umv_space;
 
+       if (hnae3_dev_fd_supported(hdev))
+               hdev->fd_en = true;
+
        ret = hclge_parse_speed(cfg.default_speed, &hdev->hw.mac.speed);
        if (ret) {
                dev_err(&hdev->pdev->dev, "Get wrong speed ret=%d.\n", ret);
@@ -1318,7 +1331,7 @@ static int  hclge_cmd_alloc_tx_buff(struct hclge_dev *hdev,
        req = (struct hclge_tx_buff_alloc_cmd *)desc.data;
 
        hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TX_BUFF_ALLOC, 0);
-       for (i = 0; i < HCLGE_TC_NUM; i++) {
+       for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
                u32 buf_size = buf_alloc->priv_buf[i].tx_buf_size;
 
                req->tx_pkt_buff[i] =
@@ -1493,13 +1506,14 @@ static int hclge_tx_buffer_calc(struct hclge_dev *hdev,
        for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
                struct hclge_priv_buf *priv = &buf_alloc->priv_buf[i];
 
-               if (total_size < hdev->tx_buf_size)
-                       return -ENOMEM;
+               if (hdev->hw_tc_map & BIT(i)) {
+                       if (total_size < hdev->tx_buf_size)
+                               return -ENOMEM;
 
-               if (hdev->hw_tc_map & BIT(i))
                        priv->tx_buf_size = hdev->tx_buf_size;
-               else
+               } else {
                        priv->tx_buf_size = 0;
+               }
 
                total_size -= priv->tx_buf_size;
        }
@@ -1507,66 +1521,15 @@ static int hclge_tx_buffer_calc(struct hclge_dev *hdev,
        return 0;
 }
 
-/* hclge_rx_buffer_calc: calculate the rx private buffer size for all TCs
- * @hdev: pointer to struct hclge_dev
- * @buf_alloc: pointer to buffer calculation data
- * @return: 0: calculate sucessful, negative: fail
- */
-static int hclge_rx_buffer_calc(struct hclge_dev *hdev,
-                               struct hclge_pkt_buf_alloc *buf_alloc)
+static bool hclge_rx_buf_calc_all(struct hclge_dev *hdev, bool max,
+                                 struct hclge_pkt_buf_alloc *buf_alloc)
 {
-       u32 rx_all = hdev->pkt_buf_size, aligned_mps;
-       int no_pfc_priv_num, pfc_priv_num;
-       struct hclge_priv_buf *priv;
+       u32 rx_all = hdev->pkt_buf_size - hclge_get_tx_buff_alloced(buf_alloc);
+       u32 aligned_mps = round_up(hdev->mps, HCLGE_BUF_SIZE_UNIT);
        int i;
 
-       aligned_mps = round_up(hdev->mps, HCLGE_BUF_SIZE_UNIT);
-       rx_all -= hclge_get_tx_buff_alloced(buf_alloc);
-
-       /* When DCB is not supported, rx private
-        * buffer is not allocated.
-        */
-       if (!hnae3_dev_dcb_supported(hdev)) {
-               if (!hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all))
-                       return -ENOMEM;
-
-               return 0;
-       }
-
-       /* step 1, try to alloc private buffer for all enabled tc */
-       for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
-               priv = &buf_alloc->priv_buf[i];
-               if (hdev->hw_tc_map & BIT(i)) {
-                       priv->enable = 1;
-                       if (hdev->tm_info.hw_pfc_map & BIT(i)) {
-                               priv->wl.low = aligned_mps;
-                               priv->wl.high =
-                                       roundup(priv->wl.low + aligned_mps,
-                                               HCLGE_BUF_SIZE_UNIT);
-                               priv->buf_size = priv->wl.high +
-                                       hdev->dv_buf_size;
-                       } else {
-                               priv->wl.low = 0;
-                               priv->wl.high = 2 * aligned_mps;
-                               priv->buf_size = priv->wl.high +
-                                               hdev->dv_buf_size;
-                       }
-               } else {
-                       priv->enable = 0;
-                       priv->wl.low = 0;
-                       priv->wl.high = 0;
-                       priv->buf_size = 0;
-               }
-       }
-
-       if (hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all))
-               return 0;
-
-       /* step 2, try to decrease the buffer size of
-        * no pfc TC's private buffer
-        */
        for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
-               priv = &buf_alloc->priv_buf[i];
+               struct hclge_priv_buf *priv = &buf_alloc->priv_buf[i];
 
                priv->enable = 0;
                priv->wl.low = 0;
@@ -1579,28 +1542,30 @@ static int hclge_rx_buffer_calc(struct hclge_dev *hdev,
                priv->enable = 1;
 
                if (hdev->tm_info.hw_pfc_map & BIT(i)) {
-                       priv->wl.low = 256;
-                       priv->wl.high = priv->wl.low + aligned_mps;
-                       priv->buf_size = priv->wl.high + hdev->dv_buf_size;
+                       priv->wl.low = max ? aligned_mps : 256;
+                       priv->wl.high = roundup(priv->wl.low + aligned_mps,
+                                               HCLGE_BUF_SIZE_UNIT);
                } else {
                        priv->wl.low = 0;
-                       priv->wl.high = aligned_mps;
-                       priv->buf_size = priv->wl.high + hdev->dv_buf_size;
+                       priv->wl.high = max ? (aligned_mps * 2) : aligned_mps;
                }
+
+               priv->buf_size = priv->wl.high + hdev->dv_buf_size;
        }
 
-       if (hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all))
-               return 0;
+       return hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all);
+}
 
-       /* step 3, try to reduce the number of pfc disabled TCs,
-        * which have private buffer
-        */
-       /* get the total no pfc enable TC number, which have private buffer */
-       no_pfc_priv_num = hclge_get_no_pfc_priv_num(hdev, buf_alloc);
+static bool hclge_drop_nopfc_buf_till_fit(struct hclge_dev *hdev,
+                                         struct hclge_pkt_buf_alloc *buf_alloc)
+{
+       u32 rx_all = hdev->pkt_buf_size - hclge_get_tx_buff_alloced(buf_alloc);
+       int no_pfc_priv_num = hclge_get_no_pfc_priv_num(hdev, buf_alloc);
+       int i;
 
        /* let the last to be cleared first */
        for (i = HCLGE_MAX_TC_NUM - 1; i >= 0; i--) {
-               priv = &buf_alloc->priv_buf[i];
+               struct hclge_priv_buf *priv = &buf_alloc->priv_buf[i];
 
                if (hdev->hw_tc_map & BIT(i) &&
                    !(hdev->tm_info.hw_pfc_map & BIT(i))) {
@@ -1617,17 +1582,19 @@ static int hclge_rx_buffer_calc(struct hclge_dev *hdev,
                        break;
        }
 
-       if (hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all))
-               return 0;
+       return hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all);
+}
 
-       /* step 4, try to reduce the number of pfc enabled TCs
-        * which have private buffer.
-        */
-       pfc_priv_num = hclge_get_pfc_priv_num(hdev, buf_alloc);
+static bool hclge_drop_pfc_buf_till_fit(struct hclge_dev *hdev,
+                                       struct hclge_pkt_buf_alloc *buf_alloc)
+{
+       u32 rx_all = hdev->pkt_buf_size - hclge_get_tx_buff_alloced(buf_alloc);
+       int pfc_priv_num = hclge_get_pfc_priv_num(hdev, buf_alloc);
+       int i;
 
        /* let the last to be cleared first */
        for (i = HCLGE_MAX_TC_NUM - 1; i >= 0; i--) {
-               priv = &buf_alloc->priv_buf[i];
+               struct hclge_priv_buf *priv = &buf_alloc->priv_buf[i];
 
                if (hdev->hw_tc_map & BIT(i) &&
                    hdev->tm_info.hw_pfc_map & BIT(i)) {
@@ -1643,7 +1610,40 @@ static int hclge_rx_buffer_calc(struct hclge_dev *hdev,
                    pfc_priv_num == 0)
                        break;
        }
-       if (hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all))
+
+       return hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all);
+}
+
+/* hclge_rx_buffer_calc: calculate the rx private buffer size for all TCs
+ * @hdev: pointer to struct hclge_dev
+ * @buf_alloc: pointer to buffer calculation data
+ * @return: 0: calculate sucessful, negative: fail
+ */
+static int hclge_rx_buffer_calc(struct hclge_dev *hdev,
+                               struct hclge_pkt_buf_alloc *buf_alloc)
+{
+       /* When DCB is not supported, rx private buffer is not allocated. */
+       if (!hnae3_dev_dcb_supported(hdev)) {
+               u32 rx_all = hdev->pkt_buf_size;
+
+               rx_all -= hclge_get_tx_buff_alloced(buf_alloc);
+               if (!hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all))
+                       return -ENOMEM;
+
+               return 0;
+       }
+
+       if (hclge_rx_buf_calc_all(hdev, true, buf_alloc))
+               return 0;
+
+       /* try to decrease the buffer size */
+       if (hclge_rx_buf_calc_all(hdev, false, buf_alloc))
+               return 0;
+
+       if (hclge_drop_nopfc_buf_till_fit(hdev, buf_alloc))
+               return 0;
+
+       if (hclge_drop_pfc_buf_till_fit(hdev, buf_alloc))
                return 0;
 
        return -ENOMEM;
@@ -2599,7 +2599,7 @@ static int hclge_set_vf_rst(struct hclge_dev *hdev, int func_id, bool reset)
        return hclge_cmd_send(&hdev->hw, &desc, 1);
 }
 
-int hclge_set_all_vf_rst(struct hclge_dev *hdev, bool reset)
+static int hclge_set_all_vf_rst(struct hclge_dev *hdev, bool reset)
 {
        int i;
 
@@ -3652,8 +3652,11 @@ void hclge_rss_indir_init_cfg(struct hclge_dev *hdev)
 
 static void hclge_rss_init_cfg(struct hclge_dev *hdev)
 {
+       int i, rss_algo = HCLGE_RSS_HASH_ALGO_TOEPLITZ;
        struct hclge_vport *vport = hdev->vport;
-       int i;
+
+       if (hdev->pdev->revision >= 0x21)
+               rss_algo = HCLGE_RSS_HASH_ALGO_SIMPLE;
 
        for (i = 0; i < hdev->num_vmdq_vport + 1; i++) {
                vport[i].rss_tuple_sets.ipv4_tcp_en =
@@ -3673,9 +3676,10 @@ static void hclge_rss_init_cfg(struct hclge_dev *hdev)
                vport[i].rss_tuple_sets.ipv6_fragment_en =
                        HCLGE_RSS_INPUT_TUPLE_OTHER;
 
-               vport[i].rss_algo = HCLGE_RSS_HASH_ALGO_TOEPLITZ;
+               vport[i].rss_algo = rss_algo;
 
-               netdev_rss_key_fill(vport[i].rss_hash_key, HCLGE_RSS_KEY_SIZE);
+               memcpy(vport[i].rss_hash_key, hclge_hash_key,
+                      HCLGE_RSS_KEY_SIZE);
        }
 
        hclge_rss_indir_init_cfg(hdev);
@@ -3961,7 +3965,6 @@ static int hclge_init_fd_config(struct hclge_dev *hdev)
                return -EOPNOTSUPP;
        }
 
-       hdev->fd_cfg.fd_en = true;
        hdev->fd_cfg.proto_support =
                TCP_V4_FLOW | UDP_V4_FLOW | SCTP_V4_FLOW | TCP_V6_FLOW |
                UDP_V6_FLOW | SCTP_V6_FLOW | IPV4_USER_FLOW | IPV6_USER_FLOW;
@@ -4719,7 +4722,7 @@ static int hclge_add_fd_entry(struct hnae3_handle *handle,
        if (!hnae3_dev_fd_supported(hdev))
                return -EOPNOTSUPP;
 
-       if (!hdev->fd_cfg.fd_en) {
+       if (!hdev->fd_en) {
                dev_warn(&hdev->pdev->dev,
                         "Please enable flow director first\n");
                return -EOPNOTSUPP;
@@ -4872,7 +4875,7 @@ static int hclge_restore_fd_entries(struct hnae3_handle *handle)
                return 0;
 
        /* if fd is disabled, should not restore it when reset */
-       if (!hdev->fd_cfg.fd_en)
+       if (!hdev->fd_en)
                return 0;
 
        hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
@@ -5158,7 +5161,7 @@ static void hclge_enable_fd(struct hnae3_handle *handle, bool enable)
        struct hclge_vport *vport = hclge_get_vport(handle);
        struct hclge_dev *hdev = vport->back;
 
-       hdev->fd_cfg.fd_en = enable;
+       hdev->fd_en = enable;
        if (!enable)
                hclge_del_all_fd_entries(handle, false);
        else
@@ -5584,13 +5587,19 @@ static bool hclge_is_all_function_id_zero(struct hclge_desc *desc)
 }
 
 static void hclge_prepare_mac_addr(struct hclge_mac_vlan_tbl_entry_cmd *new_req,
-                                  const u8 *addr)
+                                  const u8 *addr, bool is_mc)
 {
        const unsigned char *mac_addr = addr;
        u32 high_val = mac_addr[2] << 16 | (mac_addr[3] << 24) |
                       (mac_addr[0]) | (mac_addr[1] << 8);
        u32 low_val  = mac_addr[4] | (mac_addr[5] << 8);
 
+       hnae3_set_bit(new_req->flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
+       if (is_mc) {
+               hnae3_set_bit(new_req->entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 1);
+               hnae3_set_bit(new_req->mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
+       }
+
        new_req->mac_addr_hi32 = cpu_to_le32(high_val);
        new_req->mac_addr_lo16 = cpu_to_le16(low_val & 0xffff);
 }
@@ -5821,9 +5830,12 @@ static void hclge_update_umv_space(struct hclge_vport *vport, bool is_free)
        if (is_free) {
                if (vport->used_umv_num > hdev->priv_umv_size)
                        hdev->share_umv_size++;
-               vport->used_umv_num--;
+
+               if (vport->used_umv_num > 0)
+                       vport->used_umv_num--;
        } else {
-               if (vport->used_umv_num >= hdev->priv_umv_size)
+               if (vport->used_umv_num >= hdev->priv_umv_size &&
+                   hdev->share_umv_size > 0)
                        hdev->share_umv_size--;
                vport->used_umv_num++;
        }
@@ -5861,14 +5873,13 @@ int hclge_add_uc_addr_common(struct hclge_vport *vport,
        }
 
        memset(&req, 0, sizeof(req));
-       hnae3_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
 
        hnae3_set_field(egress_port, HCLGE_MAC_EPORT_VFID_M,
                        HCLGE_MAC_EPORT_VFID_S, vport->vport_id);
 
        req.egress_port = cpu_to_le16(egress_port);
 
-       hclge_prepare_mac_addr(&req, addr);
+       hclge_prepare_mac_addr(&req, addr, false);
 
        /* Lookup the mac address in the mac_vlan table, and add
         * it if the entry is inexistent. Repeated unicast entry
@@ -5926,9 +5937,8 @@ int hclge_rm_uc_addr_common(struct hclge_vport *vport,
        }
 
        memset(&req, 0, sizeof(req));
-       hnae3_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
        hnae3_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0);
-       hclge_prepare_mac_addr(&req, addr);
+       hclge_prepare_mac_addr(&req, addr, false);
        ret = hclge_remove_mac_vlan_tbl(vport, &req);
        if (!ret)
                hclge_update_umv_space(vport, true);
@@ -5960,11 +5970,8 @@ int hclge_add_mc_addr_common(struct hclge_vport *vport,
                return -EINVAL;
        }
        memset(&req, 0, sizeof(req));
-       hnae3_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
        hnae3_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0);
-       hnae3_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 1);
-       hnae3_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
-       hclge_prepare_mac_addr(&req, addr);
+       hclge_prepare_mac_addr(&req, addr, true);
        status = hclge_lookup_mac_vlan_tbl(vport, &req, desc, true);
        if (!status) {
                /* This mac addr exist, update VFID for it */
@@ -6010,11 +6017,8 @@ int hclge_rm_mc_addr_common(struct hclge_vport *vport,
        }
 
        memset(&req, 0, sizeof(req));
-       hnae3_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
        hnae3_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0);
-       hnae3_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 1);
-       hnae3_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
-       hclge_prepare_mac_addr(&req, addr);
+       hclge_prepare_mac_addr(&req, addr, true);
        status = hclge_lookup_mac_vlan_tbl(vport, &req, desc, true);
        if (!status) {
                /* This mac addr exist, remove this handle's VFID for it */
@@ -7050,16 +7054,6 @@ static void hclge_get_mdix_mode(struct hnae3_handle *handle,
                *tp_mdix = ETH_TP_MDI;
 }
 
-static int hclge_init_instance_hw(struct hclge_dev *hdev)
-{
-       return hclge_mac_connect_phy(hdev);
-}
-
-static void hclge_uninit_instance_hw(struct hclge_dev *hdev)
-{
-       hclge_mac_disconnect_phy(hdev);
-}
-
 static int hclge_init_client_instance(struct hnae3_client *client,
                                      struct hnae3_ae_dev *ae_dev)
 {
@@ -7079,13 +7073,6 @@ static int hclge_init_client_instance(struct hnae3_client *client,
                        if (ret)
                                goto clear_nic;
 
-                       ret = hclge_init_instance_hw(hdev);
-                       if (ret) {
-                               client->ops->uninit_instance(&vport->nic,
-                                                            0);
-                               goto clear_nic;
-                       }
-
                        hnae3_set_client_init_flag(client, ae_dev, 1);
 
                        if (hdev->roce_client &&
@@ -7170,7 +7157,6 @@ static void hclge_uninit_client_instance(struct hnae3_client *client,
                if (client->type == HNAE3_CLIENT_ROCE)
                        return;
                if (hdev->nic_client && client->ops->uninit_instance) {
-                       hclge_uninit_instance_hw(hdev);
                        client->ops->uninit_instance(&vport->nic, 0);
                        hdev->nic_client = NULL;
                        vport->nic.client = NULL;
@@ -7389,7 +7375,7 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
        ret = hclge_init_umv_space(hdev);
        if (ret) {
                dev_err(&pdev->dev, "umv space init error, ret=%d.\n", ret);
-               goto err_msi_irq_uninit;
+               goto err_mdiobus_unreg;
        }
 
        ret = hclge_mac_init(hdev);
@@ -7991,7 +7977,7 @@ static void hclge_get_link_mode(struct hnae3_handle *handle,
        }
 }
 
-static int hclge_gro_en(struct hnae3_handle *handle, int enable)
+static int hclge_gro_en(struct hnae3_handle *handle, bool enable)
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
        struct hclge_dev *hdev = vport->back;
@@ -8076,6 +8062,8 @@ static const struct hnae3_ae_ops hclge_ops = {
        .set_gro_en = hclge_gro_en,
        .get_global_queue_id = hclge_covert_handle_qid_global,
        .set_timer_task = hclge_set_timer_task,
+       .mac_connect_phy = hclge_mac_connect_phy,
+       .mac_disconnect_phy = hclge_mac_disconnect_phy,
 };
 
 static struct hnae3_ae_algo ae_algo = {
index 2c413c6..c939f4a 100644 (file)
@@ -583,7 +583,6 @@ struct hclge_fd_key_cfg {
 
 struct hclge_fd_cfg {
        u8 fd_mode;
-       u8 fd_en;
        u16 max_key_length;
        u32 proto_support;
        u32 rule_num[2]; /* rule entry number */
@@ -758,6 +757,7 @@ struct hclge_dev {
        struct hclge_fd_cfg fd_cfg;
        struct hlist_head fd_rule_list;
        u16 hclge_fd_rule_num;
+       u8 fd_en;
 
        u16 wanted_umv_size;
        /* max available unicast mac vlan space */
index 3603034..7e4a104 100644 (file)
@@ -319,10 +319,14 @@ static int hclge_get_vf_tcinfo(struct hclge_vport *vport,
                               struct hclge_mbx_vf_to_pf_cmd *mbx_req,
                               bool gen_resp)
 {
-       struct hclge_dev *hdev = vport->back;
-       int ret;
+       struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo;
+       u8 vf_tc_map = 0;
+       int i, ret;
 
-       ret = hclge_gen_resp_to_vf(vport, mbx_req, 0, &hdev->hw_tc_map,
+       for (i = 0; i < kinfo->num_tc; i++)
+               vf_tc_map |= BIT(i);
+
+       ret = hclge_gen_resp_to_vf(vport, mbx_req, 0, &vf_tc_map,
                                   sizeof(u8));
 
        return ret;
@@ -351,16 +355,19 @@ static int hclge_get_link_info(struct hclge_vport *vport,
 {
        struct hclge_dev *hdev = vport->back;
        u16 link_status;
-       u8 msg_data[8];
+       u8 msg_data[10];
+       u16 media_type;
        u8 dest_vfid;
        u16 duplex;
 
        /* mac.link can only be 0 or 1 */
        link_status = (u16)hdev->hw.mac.link;
        duplex = hdev->hw.mac.duplex;
+       media_type = hdev->hw.mac.media_type;
        memcpy(&msg_data[0], &link_status, sizeof(u16));
        memcpy(&msg_data[2], &hdev->hw.mac.speed, sizeof(u32));
        memcpy(&msg_data[6], &duplex, sizeof(u16));
+       memcpy(&msg_data[8], &media_type, sizeof(u16));
        dest_vfid = mbx_req->mbx_src_vfid;
 
        /* send this requested info to VF */
@@ -368,6 +375,29 @@ static int hclge_get_link_info(struct hclge_vport *vport,
                                  HCLGE_MBX_LINK_STAT_CHANGE, dest_vfid);
 }
 
+static void hclge_get_link_mode(struct hclge_vport *vport,
+                               struct hclge_mbx_vf_to_pf_cmd *mbx_req)
+{
+#define HCLGE_SUPPORTED   1
+       struct hclge_dev *hdev = vport->back;
+       unsigned long advertising;
+       unsigned long supported;
+       unsigned long send_data;
+       u8 msg_data[10];
+       u8 dest_vfid;
+
+       advertising = hdev->hw.mac.advertising[0];
+       supported = hdev->hw.mac.supported[0];
+       dest_vfid = mbx_req->mbx_src_vfid;
+       msg_data[0] = mbx_req->msg[2];
+
+       send_data = msg_data[0] == HCLGE_SUPPORTED ? supported : advertising;
+
+       memcpy(&msg_data[2], &send_data, sizeof(unsigned long));
+       hclge_send_mbx_msg(vport, msg_data, sizeof(msg_data),
+                          HCLGE_MBX_LINK_STAT_MODE, dest_vfid);
+}
+
 static void hclge_mbx_reset_vf_queue(struct hclge_vport *vport,
                                     struct hclge_mbx_vf_to_pf_cmd *mbx_req)
 {
@@ -552,6 +582,9 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
                                        "PF failed(%d) to get qid for VF\n",
                                        ret);
                        break;
+               case HCLGE_MBX_GET_LINK_MODE:
+                       hclge_get_link_mode(vport, req);
+                       break;
                default:
                        dev_err(&hdev->pdev->dev,
                                "un-supported mailbox message, code = %d\n",
index dabb843..84f2878 100644 (file)
@@ -195,8 +195,10 @@ static void hclge_mac_adjust_link(struct net_device *netdev)
                netdev_err(netdev, "failed to configure flow control.\n");
 }
 
-int hclge_mac_connect_phy(struct hclge_dev *hdev)
+int hclge_mac_connect_phy(struct hnae3_handle *handle)
 {
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
        struct net_device *netdev = hdev->vport[0].nic.netdev;
        struct phy_device *phydev = hdev->hw.mac.phydev;
        __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
@@ -229,8 +231,10 @@ int hclge_mac_connect_phy(struct hclge_dev *hdev)
        return 0;
 }
 
-void hclge_mac_disconnect_phy(struct hclge_dev *hdev)
+void hclge_mac_disconnect_phy(struct hnae3_handle *handle)
 {
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
        struct phy_device *phydev = hdev->hw.mac.phydev;
 
        if (!phydev)
index 5fbf7dd..ef095d9 100644 (file)
@@ -5,8 +5,8 @@
 #define __HCLGE_MDIO_H
 
 int hclge_mac_mdio_config(struct hclge_dev *hdev);
-int hclge_mac_connect_phy(struct hclge_dev *hdev);
-void hclge_mac_disconnect_phy(struct hclge_dev *hdev);
+int hclge_mac_connect_phy(struct hnae3_handle *handle);
+void hclge_mac_disconnect_phy(struct hnae3_handle *handle);
 void hclge_mac_start_phy(struct hclge_dev *hdev);
 void hclge_mac_stop_phy(struct hclge_dev *hdev);
 
index 9f4069f..aafc69f 100644 (file)
@@ -520,8 +520,14 @@ static void hclge_tm_vport_tc_info_update(struct hclge_vport *vport)
        u16 max_rss_size;
        u8 i;
 
-       vport->bw_limit = hdev->tm_info.pg_info[0].bw_limit;
-       kinfo->num_tc = min_t(u16, vport->alloc_tqps, hdev->tm_info.num_tc);
+       /* TC configuration is shared by PF/VF in one port, only allow
+        * one tc for VF for simplicity. VF's vport_id is non zero.
+        */
+       kinfo->num_tc = vport->vport_id ? 1 :
+                       min_t(u16, vport->alloc_tqps, hdev->tm_info.num_tc);
+       vport->qs_offset = (vport->vport_id ? hdev->tm_info.num_tc : 0) +
+                               (vport->vport_id ? (vport->vport_id - 1) : 0);
+
        max_rss_size = min_t(u16, hdev->rss_size_max,
                             vport->alloc_tqps / kinfo->num_tc);
 
@@ -538,12 +544,12 @@ static void hclge_tm_vport_tc_info_update(struct hclge_vport *vport)
        }
 
        kinfo->num_tqps = kinfo->num_tc * kinfo->rss_size;
-       vport->qs_offset = hdev->tm_info.num_tc * vport->vport_id;
        vport->dwrr = 100;  /* 100 percent as init */
        vport->alloc_rss_size = kinfo->rss_size;
+       vport->bw_limit = hdev->tm_info.pg_info[0].bw_limit;
 
        for (i = 0; i < HNAE3_MAX_TC; i++) {
-               if (hdev->hw_tc_map & BIT(i)) {
+               if (hdev->hw_tc_map & BIT(i) && i < kinfo->num_tc) {
                        kinfo->tc_info[i].enable = true;
                        kinfo->tc_info[i].tqp_offset = i * kinfo->rss_size;
                        kinfo->tc_info[i].tqp_count = kinfo->rss_size;
@@ -766,13 +772,17 @@ static int hclge_tm_pri_q_qs_cfg(struct hclge_dev *hdev)
 
        if (hdev->tx_sch_mode == HCLGE_FLAG_TC_BASE_SCH_MODE) {
                /* Cfg qs -> pri mapping, one by one mapping */
-               for (k = 0; k < hdev->num_alloc_vport; k++)
-                       for (i = 0; i < hdev->tm_info.num_tc; i++) {
+               for (k = 0; k < hdev->num_alloc_vport; k++) {
+                       struct hnae3_knic_private_info *kinfo =
+                               &vport[k].nic.kinfo;
+
+                       for (i = 0; i < kinfo->num_tc; i++) {
                                ret = hclge_tm_qs_to_pri_map_cfg(
                                        hdev, vport[k].qs_offset + i, i);
                                if (ret)
                                        return ret;
                        }
+               }
        } else if (hdev->tx_sch_mode == HCLGE_FLAG_VNET_BASE_SCH_MODE) {
                /* Cfg qs -> pri mapping,  qs = tc, pri = vf, 8 qs -> 1 pri */
                for (k = 0; k < hdev->num_alloc_vport; k++)
index fc99a0c..4a897cf 100644 (file)
@@ -21,6 +21,14 @@ static const struct pci_device_id ae_algovf_pci_tbl[] = {
        {0, }
 };
 
+static const u8 hclgevf_hash_key[] = {
+       0x6D, 0x5A, 0x56, 0xDA, 0x25, 0x5B, 0x0E, 0xC2,
+       0x41, 0x67, 0x25, 0x3D, 0x43, 0xA3, 0x8F, 0xB0,
+       0xD0, 0xCA, 0x2B, 0xCB, 0xAE, 0x7B, 0x30, 0xB4,
+       0x77, 0xCB, 0x2D, 0xA3, 0x80, 0x30, 0xF2, 0x0C,
+       0x6A, 0x42, 0xB7, 0x3B, 0xBE, 0xAC, 0x01, 0xFA
+};
+
 MODULE_DEVICE_TABLE(pci, ae_algovf_pci_tbl);
 
 static const u32 cmdq_reg_addr_list[] = {HCLGEVF_CMDQ_TX_ADDR_L_REG,
@@ -78,7 +86,12 @@ static const u32 tqp_intr_reg_addr_list[] = {HCLGEVF_TQP_INTR_CTRL_REG,
 static inline struct hclgevf_dev *hclgevf_ae_get_hdev(
        struct hnae3_handle *handle)
 {
-       return container_of(handle, struct hclgevf_dev, nic);
+       if (!handle->client)
+               return container_of(handle, struct hclgevf_dev, nic);
+       else if (handle->client->type == HNAE3_CLIENT_ROCE)
+               return container_of(handle, struct hclgevf_dev, roce);
+       else
+               return container_of(handle, struct hclgevf_dev, nic);
 }
 
 static int hclgevf_tqps_update_stats(struct hnae3_handle *handle)
@@ -368,6 +381,21 @@ void hclgevf_update_link_status(struct hclgevf_dev *hdev, int link_state)
        }
 }
 
+void hclgevf_update_link_mode(struct hclgevf_dev *hdev)
+{
+#define HCLGEVF_ADVERTISING 0
+#define HCLGEVF_SUPPORTED   1
+       u8 send_msg;
+       u8 resp_msg;
+
+       send_msg = HCLGEVF_ADVERTISING;
+       hclgevf_send_mbx_msg(hdev, HCLGE_MBX_GET_LINK_MODE, 0, &send_msg,
+                            sizeof(u8), false, &resp_msg, sizeof(u8));
+       send_msg = HCLGEVF_SUPPORTED;
+       hclgevf_send_mbx_msg(hdev, HCLGE_MBX_GET_LINK_MODE, 0, &send_msg,
+                            sizeof(u8), false, &resp_msg, sizeof(u8));
+}
+
 static int hclgevf_set_handle_info(struct hclgevf_dev *hdev)
 {
        struct hnae3_handle *nic = &hdev->nic;
@@ -1611,6 +1639,10 @@ static void hclgevf_keep_alive_task(struct work_struct *work)
        int ret;
 
        hdev = container_of(work, struct hclgevf_dev, keep_alive_task);
+
+       if (test_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state))
+               return;
+
        ret = hclgevf_send_mbx_msg(hdev, HCLGE_MBX_KEEP_ALIVE, 0, NULL,
                                   0, false, &respmsg, sizeof(u8));
        if (ret)
@@ -1629,6 +1661,8 @@ static void hclgevf_service_task(struct work_struct *work)
         */
        hclgevf_request_link_info(hdev);
 
+       hclgevf_update_link_mode(hdev);
+
        hclgevf_deferred_task_schedule(hdev);
 
        clear_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state);
@@ -1709,8 +1743,6 @@ static int hclgevf_configure(struct hclgevf_dev *hdev)
 {
        int ret;
 
-       hdev->hw.mac.media_type = HNAE3_MEDIA_TYPE_NONE;
-
        /* get queue configuration from PF */
        ret = hclgevf_get_queue_info(hdev);
        if (ret)
@@ -1789,9 +1821,9 @@ static int hclgevf_rss_init_hw(struct hclgevf_dev *hdev)
        rss_cfg->rss_size = hdev->rss_size_max;
 
        if (hdev->pdev->revision >= 0x21) {
-               rss_cfg->hash_algo = HCLGEVF_RSS_HASH_ALGO_TOEPLITZ;
-               netdev_rss_key_fill(rss_cfg->rss_hash_key,
-                                   HCLGEVF_RSS_KEY_SIZE);
+               rss_cfg->hash_algo = HCLGEVF_RSS_HASH_ALGO_SIMPLE;
+               memcpy(rss_cfg->rss_hash_key, hclgevf_hash_key,
+                      HCLGEVF_RSS_KEY_SIZE);
 
                ret = hclgevf_set_rss_algo_key(hdev, rss_cfg->hash_algo,
                                               rss_cfg->rss_hash_key);
@@ -1863,6 +1895,8 @@ static int hclgevf_ae_start(struct hnae3_handle *handle)
 
        hclgevf_request_link_info(hdev);
 
+       hclgevf_update_link_mode(hdev);
+
        clear_bit(HCLGEVF_STATE_DOWN, &hdev->state);
 
        return 0;
@@ -2533,7 +2567,7 @@ void hclgevf_update_speed_duplex(struct hclgevf_dev *hdev, u32 speed,
        hdev->hw.mac.duplex = duplex;
 }
 
-static int hclgevf_gro_en(struct hnae3_handle *handle, int enable)
+static int hclgevf_gro_en(struct hnae3_handle *handle, bool enable)
 {
        struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
 
@@ -2569,6 +2603,16 @@ static unsigned long hclgevf_ae_dev_reset_cnt(struct hnae3_handle *handle)
        return hdev->reset_count;
 }
 
+static void hclgevf_get_link_mode(struct hnae3_handle *handle,
+                                 unsigned long *supported,
+                                 unsigned long *advertising)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       *supported = hdev->hw.mac.supported;
+       *advertising = hdev->hw.mac.advertising;
+}
+
 #define MAX_SEPARATE_NUM       4
 #define SEPARATOR_VALUE                0xFFFFFFFF
 #define REG_NUM_PER_LINE       4
@@ -2687,6 +2731,7 @@ static const struct hnae3_ae_ops hclgevf_ops = {
        .set_mtu = hclgevf_set_mtu,
        .get_global_queue_id = hclgevf_get_qid_global,
        .set_timer_task = hclgevf_set_timer_task,
+       .get_link_mode = hclgevf_get_link_mode,
 };
 
 static struct hnae3_ae_algo ae_algovf = {
index 787bc06..eba1118 100644 (file)
@@ -145,6 +145,8 @@ struct hclgevf_mac {
        int link;
        u8 duplex;
        u32 speed;
+       u64 supported;
+       u64 advertising;
 };
 
 struct hclgevf_hw {
index 84653f5..7dc3c9f 100644 (file)
@@ -197,6 +197,7 @@ void hclgevf_mbx_handler(struct hclgevf_dev *hdev)
                        break;
                case HCLGE_MBX_LINK_STAT_CHANGE:
                case HCLGE_MBX_ASSERTING_RESET:
+               case HCLGE_MBX_LINK_STAT_MODE:
                        /* set this mbx event as pending. This is required as we
                         * might loose interrupt event when mbx task is busy
                         * handling. This shall be cleared when mbx task just
@@ -247,6 +248,7 @@ void hclgevf_mbx_async_handler(struct hclgevf_dev *hdev)
        u8 duplex;
        u32 speed;
        u32 tail;
+       u8 idx;
 
        /* we can safely clear it now as we are at start of the async message
         * processing
@@ -270,12 +272,22 @@ void hclgevf_mbx_async_handler(struct hclgevf_dev *hdev)
                        link_status = le16_to_cpu(msg_q[1]);
                        memcpy(&speed, &msg_q[2], sizeof(speed));
                        duplex = (u8)le16_to_cpu(msg_q[4]);
+                       hdev->hw.mac.media_type = (u8)le16_to_cpu(msg_q[5]);
 
                        /* update upper layer with new link link status */
                        hclgevf_update_link_status(hdev, link_status);
                        hclgevf_update_speed_duplex(hdev, speed, duplex);
 
                        break;
+               case HCLGE_MBX_LINK_STAT_MODE:
+                       idx = (u8)le16_to_cpu(msg_q[1]);
+                       if (idx)
+                               memcpy(&hdev->hw.mac.supported, &msg_q[2],
+                                      sizeof(unsigned long));
+                       else
+                               memcpy(&hdev->hw.mac.advertising, &msg_q[2],
+                                      sizeof(unsigned long));
+                       break;
                case HCLGE_MBX_ASSERTING_RESET:
                        /* PF has asserted reset hence VF should go in pending
                         * state and poll for the hardware reset status till it
index 017e084..baf5cc2 100644 (file)
@@ -321,7 +321,7 @@ static int hns_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
                }
 
                hns_mdio_cmd_write(mdio_dev, is_c45,
-                                  MDIO_C45_WRITE_ADDR, phy_id, devad);
+                                  MDIO_C45_READ, phy_id, devad);
        }
 
        /* Step 5: waitting for MDIO_COMMAND_REG 's mdio_start==0,*/
index d719668..9292975 100644 (file)
@@ -1310,7 +1310,7 @@ static irqreturn_t i596_interrupt(int irq, void *dev_id)
                                                dev->stats.tx_aborted_errors++;
                                }
 
-                               dev_kfree_skb_irq(skb);
+                               dev_consume_skb_irq(skb);
 
                                tx_cmd->cmd.command = 0; /* Mark free */
                                break;
index 2f7ae11..1274ad2 100644 (file)
@@ -1194,7 +1194,7 @@ static irqreturn_t i596_interrupt(int irq, void *dev_id)
                                dma_unmap_single(dev->dev.parent,
                                                 tx_cmd->dma_addr,
                                                 skb->len, DMA_TO_DEVICE);
-                               dev_kfree_skb_irq(skb);
+                               dev_consume_skb_irq(skb);
 
                                tx_cmd->cmd.command = 0; /* Mark free */
                                break;
index 90d4919..eacf7e1 100644 (file)
@@ -28,18 +28,6 @@ config IBM_EMAC_RX_COPY_THRESHOLD
        depends on IBM_EMAC
        default "256"
 
-config IBM_EMAC_RX_SKB_HEADROOM
-       int "Additional RX skb headroom (bytes)"
-       depends on IBM_EMAC
-       default "0"
-       help
-         Additional receive skb headroom. Note, that driver
-         will always reserve at least 2 bytes to make IP header
-         aligned, so usually there is no need to add any additional
-         headroom.
-
-         If unsure, set to 0.
-
 config IBM_EMAC_DEBUG
        bool "Debugging"
        depends on IBM_EMAC
index 2092554..3c2a575 100644 (file)
@@ -1071,7 +1071,9 @@ static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu)
 
        /* Second pass, allocate new skbs */
        for (i = 0; i < NUM_RX_BUFF; ++i) {
-               struct sk_buff *skb = alloc_skb(rx_skb_size, GFP_ATOMIC);
+               struct sk_buff *skb;
+
+               skb = netdev_alloc_skb_ip_align(dev->ndev, rx_skb_size);
                if (!skb) {
                        ret = -ENOMEM;
                        goto oom;
@@ -1080,10 +1082,10 @@ static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu)
                BUG_ON(!dev->rx_skb[i]);
                dev_kfree_skb(dev->rx_skb[i]);
 
-               skb_reserve(skb, EMAC_RX_SKB_HEADROOM + 2);
                dev->rx_desc[i].data_ptr =
-                   dma_map_single(&dev->ofdev->dev, skb->data - 2, rx_sync_size,
-                                  DMA_FROM_DEVICE) + 2;
+                   dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
+                                  rx_sync_size, DMA_FROM_DEVICE)
+                                  + NET_IP_ALIGN;
                dev->rx_skb[i] = skb;
        }
  skip:
@@ -1174,20 +1176,18 @@ static void emac_clean_rx_ring(struct emac_instance *dev)
        }
 }
 
-static inline int emac_alloc_rx_skb(struct emac_instance *dev, int slot,
-                                   gfp_t flags)
+static int
+__emac_prepare_rx_skb(struct sk_buff *skb, struct emac_instance *dev, int slot)
 {
-       struct sk_buff *skb = alloc_skb(dev->rx_skb_size, flags);
        if (unlikely(!skb))
                return -ENOMEM;
 
        dev->rx_skb[slot] = skb;
        dev->rx_desc[slot].data_len = 0;
 
-       skb_reserve(skb, EMAC_RX_SKB_HEADROOM + 2);
        dev->rx_desc[slot].data_ptr =
-           dma_map_single(&dev->ofdev->dev, skb->data - 2, dev->rx_sync_size,
-                          DMA_FROM_DEVICE) + 2;
+           dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
+                          dev->rx_sync_size, DMA_FROM_DEVICE) + NET_IP_ALIGN;
        wmb();
        dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY |
            (slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
@@ -1195,6 +1195,27 @@ static inline int emac_alloc_rx_skb(struct emac_instance *dev, int slot,
        return 0;
 }
 
+static int
+emac_alloc_rx_skb(struct emac_instance *dev, int slot)
+{
+       struct sk_buff *skb;
+
+       skb = __netdev_alloc_skb_ip_align(dev->ndev, dev->rx_skb_size,
+                                         GFP_KERNEL);
+
+       return __emac_prepare_rx_skb(skb, dev, slot);
+}
+
+static int
+emac_alloc_rx_skb_napi(struct emac_instance *dev, int slot)
+{
+       struct sk_buff *skb;
+
+       skb = napi_alloc_skb(&dev->mal->napi, dev->rx_skb_size);
+
+       return __emac_prepare_rx_skb(skb, dev, slot);
+}
+
 static void emac_print_link_status(struct emac_instance *dev)
 {
        if (netif_carrier_ok(dev->ndev))
@@ -1225,7 +1246,7 @@ static int emac_open(struct net_device *ndev)
 
        /* Allocate RX ring */
        for (i = 0; i < NUM_RX_BUFF; ++i)
-               if (emac_alloc_rx_skb(dev, i, GFP_KERNEL)) {
+               if (emac_alloc_rx_skb(dev, i)) {
                        printk(KERN_ERR "%s: failed to allocate RX ring\n",
                               ndev->name);
                        goto oom;
@@ -1660,8 +1681,9 @@ static inline void emac_recycle_rx_skb(struct emac_instance *dev, int slot,
        DBG2(dev, "recycle %d %d" NL, slot, len);
 
        if (len)
-               dma_map_single(&dev->ofdev->dev, skb->data - 2,
-                              EMAC_DMA_ALIGN(len + 2), DMA_FROM_DEVICE);
+               dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
+                              SKB_DATA_ALIGN(len + NET_IP_ALIGN),
+                              DMA_FROM_DEVICE);
 
        dev->rx_desc[slot].data_len = 0;
        wmb();
@@ -1713,7 +1735,7 @@ static inline int emac_rx_sg_append(struct emac_instance *dev, int slot)
                int len = dev->rx_desc[slot].data_len;
                int tot_len = dev->rx_sg_skb->len + len;
 
-               if (unlikely(tot_len + 2 > dev->rx_skb_size)) {
+               if (unlikely(tot_len + NET_IP_ALIGN > dev->rx_skb_size)) {
                        ++dev->estats.rx_dropped_mtu;
                        dev_kfree_skb(dev->rx_sg_skb);
                        dev->rx_sg_skb = NULL;
@@ -1769,16 +1791,18 @@ static int emac_poll_rx(void *param, int budget)
                }
 
                if (len && len < EMAC_RX_COPY_THRESH) {
-                       struct sk_buff *copy_skb =
-                           alloc_skb(len + EMAC_RX_SKB_HEADROOM + 2, GFP_ATOMIC);
+                       struct sk_buff *copy_skb;
+
+                       copy_skb = napi_alloc_skb(&dev->mal->napi, len);
                        if (unlikely(!copy_skb))
                                goto oom;
 
-                       skb_reserve(copy_skb, EMAC_RX_SKB_HEADROOM + 2);
-                       memcpy(copy_skb->data - 2, skb->data - 2, len + 2);
+                       memcpy(copy_skb->data - NET_IP_ALIGN,
+                              skb->data - NET_IP_ALIGN,
+                              len + NET_IP_ALIGN);
                        emac_recycle_rx_skb(dev, slot, len);
                        skb = copy_skb;
-               } else if (unlikely(emac_alloc_rx_skb(dev, slot, GFP_ATOMIC)))
+               } else if (unlikely(emac_alloc_rx_skb_napi(dev, slot)))
                        goto oom;
 
                skb_put(skb, len);
@@ -1799,7 +1823,7 @@ static int emac_poll_rx(void *param, int budget)
        sg:
                if (ctrl & MAL_RX_CTRL_FIRST) {
                        BUG_ON(dev->rx_sg_skb);
-                       if (unlikely(emac_alloc_rx_skb(dev, slot, GFP_ATOMIC))) {
+                       if (unlikely(emac_alloc_rx_skb_napi(dev, slot))) {
                                DBG(dev, "rx OOM %d" NL, slot);
                                ++dev->estats.rx_dropped_oom;
                                emac_recycle_rx_skb(dev, slot, 0);
index 84caa4a..187689c 100644 (file)
@@ -68,22 +68,18 @@ static inline int emac_rx_size(int mtu)
                return mal_rx_size(ETH_DATA_LEN + EMAC_MTU_OVERHEAD);
 }
 
-#define EMAC_DMA_ALIGN(x)              ALIGN((x), dma_get_cache_alignment())
-
-#define EMAC_RX_SKB_HEADROOM           \
-       EMAC_DMA_ALIGN(CONFIG_IBM_EMAC_RX_SKB_HEADROOM)
-
 /* Size of RX skb for the given MTU */
 static inline int emac_rx_skb_size(int mtu)
 {
        int size = max(mtu + EMAC_MTU_OVERHEAD, emac_rx_size(mtu));
-       return EMAC_DMA_ALIGN(size + 2) + EMAC_RX_SKB_HEADROOM;
+
+       return SKB_DATA_ALIGN(size + NET_IP_ALIGN) + NET_SKB_PAD;
 }
 
 /* RX DMA sync size */
 static inline int emac_rx_sync_size(int mtu)
 {
-       return EMAC_DMA_ALIGN(emac_rx_size(mtu) + 2);
+       return SKB_DATA_ALIGN(emac_rx_size(mtu) + NET_IP_ALIGN);
 }
 
 /* Driver statistcs is split into two parts to make it more cache friendly:
index 257bd59..f86d556 100644 (file)
@@ -696,11 +696,16 @@ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw)
        ret_val =
            e1000_read_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
                                            &kum_reg_data);
-       if (ret_val)
-               return ret_val;
-       kum_reg_data |= E1000_KMRNCTRLSTA_IBIST_DISABLE;
-       e1000_write_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
-                                        kum_reg_data);
+       if (!ret_val) {
+               kum_reg_data |= E1000_KMRNCTRLSTA_IBIST_DISABLE;
+               ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
+                                                E1000_KMRNCTRLSTA_INBAND_PARAM,
+                                                kum_reg_data);
+               if (ret_val)
+                       e_dbg("Error disabling far-end loopback\n");
+       } else {
+               e_dbg("Error disabling far-end loopback\n");
+       }
 
        ret_val = e1000e_get_auto_rd_done(hw);
        if (ret_val)
@@ -754,11 +759,19 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw)
                return ret_val;
 
        /* Disable IBIST slave mode (far-end loopback) */
-       e1000_read_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
-                                       &kum_reg_data);
-       kum_reg_data |= E1000_KMRNCTRLSTA_IBIST_DISABLE;
-       e1000_write_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
-                                        kum_reg_data);
+       ret_val =
+           e1000_read_kmrn_reg_80003es2lan(hw, E1000_KMRNCTRLSTA_INBAND_PARAM,
+                                           &kum_reg_data);
+       if (!ret_val) {
+               kum_reg_data |= E1000_KMRNCTRLSTA_IBIST_DISABLE;
+               ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
+                                                E1000_KMRNCTRLSTA_INBAND_PARAM,
+                                                kum_reg_data);
+               if (ret_val)
+                       e_dbg("Error disabling far-end loopback\n");
+       } else {
+               e_dbg("Error disabling far-end loopback\n");
+       }
 
        /* Set the transmit descriptor write-back policy */
        reg_data = er32(TXDCTL(0));
index 189f231..736fa51 100644 (file)
@@ -5309,8 +5309,13 @@ static void e1000_watchdog_task(struct work_struct *work)
                        /* 8000ES2LAN requires a Rx packet buffer work-around
                         * on link down event; reset the controller to flush
                         * the Rx packet buffer.
+                        *
+                        * If the link is lost the controller stops DMA, but
+                        * if there is queued Tx work it cannot be done.  So
+                        * reset the controller to flush the Tx packet buffers.
                         */
-                       if (adapter->flags & FLAG_RX_NEEDS_RESTART)
+                       if ((adapter->flags & FLAG_RX_NEEDS_RESTART) ||
+                           e1000_desc_unused(tx_ring) + 1 < tx_ring->count)
                                adapter->flags |= FLAG_RESTART_NOW;
                        else
                                pm_schedule_suspend(netdev->dev.parent,
@@ -5333,14 +5338,6 @@ link_up:
        adapter->gotc_old = adapter->stats.gotc;
        spin_unlock(&adapter->stats64_lock);
 
-       /* If the link is lost the controller stops DMA, but
-        * if there is queued Tx work it cannot be done.  So
-        * reset the controller to flush the Tx packet buffers.
-        */
-       if (!netif_carrier_ok(netdev) &&
-           (e1000_desc_unused(tx_ring) + 1 < tx_ring->count))
-               adapter->flags |= FLAG_RESTART_NOW;
-
        /* If reset is necessary, do it outside of interrupt context. */
        if (adapter->flags & FLAG_RESTART_NOW) {
                schedule_work(&adapter->reset_task);
@@ -7351,6 +7348,8 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        e1000_print_device_info(adapter);
 
+       dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
+
        if (pci_dev_run_wake(pdev))
                pm_runtime_put_noidle(&pdev->dev);
 
index 6fd15a7..5a04194 100644 (file)
@@ -1603,14 +1603,12 @@ static int fm10k_alloc_q_vector(struct fm10k_intfc *interface,
 {
        struct fm10k_q_vector *q_vector;
        struct fm10k_ring *ring;
-       int ring_count, size;
+       int ring_count;
 
        ring_count = txr_count + rxr_count;
-       size = sizeof(struct fm10k_q_vector) +
-              (sizeof(struct fm10k_ring) * ring_count);
 
        /* allocate q_vector and rings */
-       q_vector = kzalloc(size, GFP_KERNEL);
+       q_vector = kzalloc(struct_size(q_vector, ring, ring_count), GFP_KERNEL);
        if (!q_vector)
                return -ENOMEM;
 
index 8f0a99b..cb4d026 100644 (file)
@@ -1148,7 +1148,7 @@ static void fm10k_iov_update_stats_pf(struct fm10k_hw *hw,
  *  @results: Pointer array to message, results[0] is pointer to message
  *  @mbx: Pointer to mailbox information structure
  *
- *  This function is a default handler for MSI-X requests from the VF.  The
+ *  This function is a default handler for MSI-X requests from the VF. The
  *  assumption is that in this case it is acceptable to just directly
  *  hand off the message from the VF to the underlying shared code.
  **/
index 5c6731b..5e74a51 100644 (file)
@@ -7169,11 +7169,13 @@ static int i40e_parse_cls_flower(struct i40e_vsi *vsi,
                                 struct tc_cls_flower_offload *f,
                                 struct i40e_cloud_filter *filter)
 {
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
+       struct flow_dissector *dissector = rule->match.dissector;
        u16 n_proto_mask = 0, n_proto_key = 0, addr_type = 0;
        struct i40e_pf *pf = vsi->back;
        u8 field_flags = 0;
 
-       if (f->dissector->used_keys &
+       if (dissector->used_keys &
            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
              BIT(FLOW_DISSECTOR_KEY_BASIC) |
              BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
@@ -7183,143 +7185,109 @@ static int i40e_parse_cls_flower(struct i40e_vsi *vsi,
              BIT(FLOW_DISSECTOR_KEY_PORTS) |
              BIT(FLOW_DISSECTOR_KEY_ENC_KEYID))) {
                dev_err(&pf->pdev->dev, "Unsupported key used: 0x%x\n",
-                       f->dissector->used_keys);
+                       dissector->used_keys);
                return -EOPNOTSUPP;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
-               struct flow_dissector_key_keyid *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_KEYID,
-                                                 f->key);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+               struct flow_match_enc_keyid match;
 
-               struct flow_dissector_key_keyid *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_KEYID,
-                                                 f->mask);
-
-               if (mask->keyid != 0)
+               flow_rule_match_enc_keyid(rule, &match);
+               if (match.mask->keyid != 0)
                        field_flags |= I40E_CLOUD_FIELD_TEN_ID;
 
-               filter->tenant_id = be32_to_cpu(key->keyid);
+               filter->tenant_id = be32_to_cpu(match.key->keyid);
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->key);
-
-               struct flow_dissector_key_basic *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
 
-               n_proto_key = ntohs(key->n_proto);
-               n_proto_mask = ntohs(mask->n_proto);
+               flow_rule_match_basic(rule, &match);
+               n_proto_key = ntohs(match.key->n_proto);
+               n_proto_mask = ntohs(match.mask->n_proto);
 
                if (n_proto_key == ETH_P_ALL) {
                        n_proto_key = 0;
                        n_proto_mask = 0;
                }
                filter->n_proto = n_proto_key & n_proto_mask;
-               filter->ip_proto = key->ip_proto;
+               filter->ip_proto = match.key->ip_proto;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
-               struct flow_dissector_key_eth_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
-                                                 f->key);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct flow_match_eth_addrs match;
 
-               struct flow_dissector_key_eth_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
-                                                 f->mask);
+               flow_rule_match_eth_addrs(rule, &match);
 
                /* use is_broadcast and is_zero to check for all 0xf or 0 */
-               if (!is_zero_ether_addr(mask->dst)) {
-                       if (is_broadcast_ether_addr(mask->dst)) {
+               if (!is_zero_ether_addr(match.mask->dst)) {
+                       if (is_broadcast_ether_addr(match.mask->dst)) {
                                field_flags |= I40E_CLOUD_FIELD_OMAC;
                        } else {
                                dev_err(&pf->pdev->dev, "Bad ether dest mask %pM\n",
-                                       mask->dst);
+                                       match.mask->dst);
                                return I40E_ERR_CONFIG;
                        }
                }
 
-               if (!is_zero_ether_addr(mask->src)) {
-                       if (is_broadcast_ether_addr(mask->src)) {
+               if (!is_zero_ether_addr(match.mask->src)) {
+                       if (is_broadcast_ether_addr(match.mask->src)) {
                                field_flags |= I40E_CLOUD_FIELD_IMAC;
                        } else {
                                dev_err(&pf->pdev->dev, "Bad ether src mask %pM\n",
-                                       mask->src);
+                                       match.mask->src);
                                return I40E_ERR_CONFIG;
                        }
                }
-               ether_addr_copy(filter->dst_mac, key->dst);
-               ether_addr_copy(filter->src_mac, key->src);
+               ether_addr_copy(filter->dst_mac, match.key->dst);
+               ether_addr_copy(filter->src_mac, match.key->src);
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
-               struct flow_dissector_key_vlan *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_VLAN,
-                                                 f->key);
-               struct flow_dissector_key_vlan *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_VLAN,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+               struct flow_match_vlan match;
 
-               if (mask->vlan_id) {
-                       if (mask->vlan_id == VLAN_VID_MASK) {
+               flow_rule_match_vlan(rule, &match);
+               if (match.mask->vlan_id) {
+                       if (match.mask->vlan_id == VLAN_VID_MASK) {
                                field_flags |= I40E_CLOUD_FIELD_IVLAN;
 
                        } else {
                                dev_err(&pf->pdev->dev, "Bad vlan mask 0x%04x\n",
-                                       mask->vlan_id);
+                                       match.mask->vlan_id);
                                return I40E_ERR_CONFIG;
                        }
                }
 
-               filter->vlan_id = cpu_to_be16(key->vlan_id);
+               filter->vlan_id = cpu_to_be16(match.key->vlan_id);
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
-               struct flow_dissector_key_control *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_CONTROL,
-                                                 f->key);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+               struct flow_match_control match;
 
-               addr_type = key->addr_type;
+               flow_rule_match_control(rule, &match);
+               addr_type = match.key->addr_type;
        }
 
        if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
-               struct flow_dissector_key_ipv4_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                                 f->key);
-               struct flow_dissector_key_ipv4_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                                 f->mask);
-
-               if (mask->dst) {
-                       if (mask->dst == cpu_to_be32(0xffffffff)) {
+               struct flow_match_ipv4_addrs match;
+
+               flow_rule_match_ipv4_addrs(rule, &match);
+               if (match.mask->dst) {
+                       if (match.mask->dst == cpu_to_be32(0xffffffff)) {
                                field_flags |= I40E_CLOUD_FIELD_IIP;
                        } else {
                                dev_err(&pf->pdev->dev, "Bad ip dst mask %pI4b\n",
-                                       &mask->dst);
+                                       &match.mask->dst);
                                return I40E_ERR_CONFIG;
                        }
                }
 
-               if (mask->src) {
-                       if (mask->src == cpu_to_be32(0xffffffff)) {
+               if (match.mask->src) {
+                       if (match.mask->src == cpu_to_be32(0xffffffff)) {
                                field_flags |= I40E_CLOUD_FIELD_IIP;
                        } else {
                                dev_err(&pf->pdev->dev, "Bad ip src mask %pI4b\n",
-                                       &mask->src);
+                                       &match.mask->src);
                                return I40E_ERR_CONFIG;
                        }
                }
@@ -7328,70 +7296,60 @@ static int i40e_parse_cls_flower(struct i40e_vsi *vsi,
                        dev_err(&pf->pdev->dev, "Tenant id not allowed for ip filter\n");
                        return I40E_ERR_CONFIG;
                }
-               filter->dst_ipv4 = key->dst;
-               filter->src_ipv4 = key->src;
+               filter->dst_ipv4 = match.key->dst;
+               filter->src_ipv4 = match.key->src;
        }
 
        if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
-               struct flow_dissector_key_ipv6_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                                 f->key);
-               struct flow_dissector_key_ipv6_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                                 f->mask);
+               struct flow_match_ipv6_addrs match;
+
+               flow_rule_match_ipv6_addrs(rule, &match);
 
                /* src and dest IPV6 address should not be LOOPBACK
                 * (0:0:0:0:0:0:0:1), which can be represented as ::1
                 */
-               if (ipv6_addr_loopback(&key->dst) ||
-                   ipv6_addr_loopback(&key->src)) {
+               if (ipv6_addr_loopback(&match.key->dst) ||
+                   ipv6_addr_loopback(&match.key->src)) {
                        dev_err(&pf->pdev->dev,
                                "Bad ipv6, addr is LOOPBACK\n");
                        return I40E_ERR_CONFIG;
                }
-               if (!ipv6_addr_any(&mask->dst) || !ipv6_addr_any(&mask->src))
+               if (!ipv6_addr_any(&match.mask->dst) ||
+                   !ipv6_addr_any(&match.mask->src))
                        field_flags |= I40E_CLOUD_FIELD_IIP;
 
-               memcpy(&filter->src_ipv6, &key->src.s6_addr32,
+               memcpy(&filter->src_ipv6, &match.key->src.s6_addr32,
                       sizeof(filter->src_ipv6));
-               memcpy(&filter->dst_ipv6, &key->dst.s6_addr32,
+               memcpy(&filter->dst_ipv6, &match.key->dst.s6_addr32,
                       sizeof(filter->dst_ipv6));
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
-               struct flow_dissector_key_ports *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_PORTS,
-                                                 f->key);
-               struct flow_dissector_key_ports *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_PORTS,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+               struct flow_match_ports match;
 
-               if (mask->src) {
-                       if (mask->src == cpu_to_be16(0xffff)) {
+               flow_rule_match_ports(rule, &match);
+               if (match.mask->src) {
+                       if (match.mask->src == cpu_to_be16(0xffff)) {
                                field_flags |= I40E_CLOUD_FIELD_IIP;
                        } else {
                                dev_err(&pf->pdev->dev, "Bad src port mask 0x%04x\n",
-                                       be16_to_cpu(mask->src));
+                                       be16_to_cpu(match.mask->src));
                                return I40E_ERR_CONFIG;
                        }
                }
 
-               if (mask->dst) {
-                       if (mask->dst == cpu_to_be16(0xffff)) {
+               if (match.mask->dst) {
+                       if (match.mask->dst == cpu_to_be16(0xffff)) {
                                field_flags |= I40E_CLOUD_FIELD_IIP;
                        } else {
                                dev_err(&pf->pdev->dev, "Bad dst port mask 0x%04x\n",
-                                       be16_to_cpu(mask->dst));
+                                       be16_to_cpu(match.mask->dst));
                                return I40E_ERR_CONFIG;
                        }
                }
 
-               filter->dst_port = key->dst;
-               filter->src_port = key->src;
+               filter->dst_port = match.key->dst;
+               filter->src_port = match.key->src;
 
                switch (filter->ip_proto) {
                case IPPROTO_TCP:
@@ -12170,9 +12128,6 @@ static int i40e_xdp(struct net_device *dev,
        case XDP_QUERY_PROG:
                xdp->prog_id = vsi->xdp_prog ? vsi->xdp_prog->aux->id : 0;
                return 0;
-       case XDP_QUERY_XSK_UMEM:
-               return i40e_xsk_umem_query(vsi, &xdp->xsk.umem,
-                                          xdp->xsk.queue_id);
        case XDP_SETUP_XSK_UMEM:
                return i40e_xsk_umem_setup(vsi, xdp->xsk.umem,
                                           xdp->xsk.queue_id);
index 96d8494..e190a2c 100644 (file)
@@ -155,34 +155,6 @@ static int i40e_xsk_umem_disable(struct i40e_vsi *vsi, u16 qid)
 }
 
 /**
- * i40e_xsk_umem_query - Queries a certain ring/qid for its UMEM
- * @vsi: Current VSI
- * @umem: UMEM associated to the ring, if any
- * @qid: Rx ring to associate UMEM to
- *
- * This function will store, if any, the UMEM associated to certain ring.
- *
- * Returns 0 on success, <0 on failure
- **/
-int i40e_xsk_umem_query(struct i40e_vsi *vsi, struct xdp_umem **umem,
-                       u16 qid)
-{
-       struct net_device *netdev = vsi->netdev;
-       struct xdp_umem *queried_umem;
-
-       if (vsi->type != I40E_VSI_MAIN)
-               return -EINVAL;
-
-       queried_umem = xdp_get_umem_from_qid(netdev, qid);
-
-       if (!queried_umem)
-               return -EINVAL;
-
-       *umem = queried_umem;
-       return 0;
-}
-
-/**
  * i40e_xsk_umem_setup - Enable/disassociate a UMEM to/from a ring/qid
  * @vsi: Current VSI
  * @umem: UMEM to enable/associate to a ring, or NULL to disable
index 9038c5d..8cc0a2e 100644 (file)
@@ -10,8 +10,6 @@ struct zero_copy_allocator;
 
 int i40e_queue_pair_disable(struct i40e_vsi *vsi, int queue_pair);
 int i40e_queue_pair_enable(struct i40e_vsi *vsi, int queue_pair);
-int i40e_xsk_umem_query(struct i40e_vsi *vsi, struct xdp_umem **umem,
-                       u16 qid);
 int i40e_xsk_umem_setup(struct i40e_vsi *vsi, struct xdp_umem *umem,
                        u16 qid);
 void i40e_zca_free(struct zero_copy_allocator *alloc, unsigned long handle);
index 9f2b7b7..4569d69 100644 (file)
@@ -2439,6 +2439,8 @@ static int iavf_parse_cls_flower(struct iavf_adapter *adapter,
                                 struct tc_cls_flower_offload *f,
                                 struct iavf_cloud_filter *filter)
 {
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
+       struct flow_dissector *dissector = rule->match.dissector;
        u16 n_proto_mask = 0;
        u16 n_proto_key = 0;
        u8 field_flags = 0;
@@ -2447,7 +2449,7 @@ static int iavf_parse_cls_flower(struct iavf_adapter *adapter,
        int i = 0;
        struct virtchnl_filter *vf = &filter->f;
 
-       if (f->dissector->used_keys &
+       if (dissector->used_keys &
            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
              BIT(FLOW_DISSECTOR_KEY_BASIC) |
              BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
@@ -2457,32 +2459,24 @@ static int iavf_parse_cls_flower(struct iavf_adapter *adapter,
              BIT(FLOW_DISSECTOR_KEY_PORTS) |
              BIT(FLOW_DISSECTOR_KEY_ENC_KEYID))) {
                dev_err(&adapter->pdev->dev, "Unsupported key used: 0x%x\n",
-                       f->dissector->used_keys);
+                       dissector->used_keys);
                return -EOPNOTSUPP;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
-               struct flow_dissector_key_keyid *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_KEYID,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+               struct flow_match_enc_keyid match;
 
-               if (mask->keyid != 0)
+               flow_rule_match_enc_keyid(rule, &match);
+               if (match.mask->keyid != 0)
                        field_flags |= IAVF_CLOUD_FIELD_TEN_ID;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->key);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
 
-               struct flow_dissector_key_basic *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->mask);
-               n_proto_key = ntohs(key->n_proto);
-               n_proto_mask = ntohs(mask->n_proto);
+               flow_rule_match_basic(rule, &match);
+               n_proto_key = ntohs(match.key->n_proto);
+               n_proto_mask = ntohs(match.mask->n_proto);
 
                if (n_proto_key == ETH_P_ALL) {
                        n_proto_key = 0;
@@ -2496,122 +2490,103 @@ static int iavf_parse_cls_flower(struct iavf_adapter *adapter,
                        vf->flow_type = VIRTCHNL_TCP_V6_FLOW;
                }
 
-               if (key->ip_proto != IPPROTO_TCP) {
+               if (match.key->ip_proto != IPPROTO_TCP) {
                        dev_info(&adapter->pdev->dev, "Only TCP transport is supported\n");
                        return -EINVAL;
                }
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
-               struct flow_dissector_key_eth_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
-                                                 f->key);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct flow_match_eth_addrs match;
+
+               flow_rule_match_eth_addrs(rule, &match);
 
-               struct flow_dissector_key_eth_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
-                                                 f->mask);
                /* use is_broadcast and is_zero to check for all 0xf or 0 */
-               if (!is_zero_ether_addr(mask->dst)) {
-                       if (is_broadcast_ether_addr(mask->dst)) {
+               if (!is_zero_ether_addr(match.mask->dst)) {
+                       if (is_broadcast_ether_addr(match.mask->dst)) {
                                field_flags |= IAVF_CLOUD_FIELD_OMAC;
                        } else {
                                dev_err(&adapter->pdev->dev, "Bad ether dest mask %pM\n",
-                                       mask->dst);
+                                       match.mask->dst);
                                return I40E_ERR_CONFIG;
                        }
                }
 
-               if (!is_zero_ether_addr(mask->src)) {
-                       if (is_broadcast_ether_addr(mask->src)) {
+               if (!is_zero_ether_addr(match.mask->src)) {
+                       if (is_broadcast_ether_addr(match.mask->src)) {
                                field_flags |= IAVF_CLOUD_FIELD_IMAC;
                        } else {
                                dev_err(&adapter->pdev->dev, "Bad ether src mask %pM\n",
-                                       mask->src);
+                                       match.mask->src);
                                return I40E_ERR_CONFIG;
                        }
                }
 
-               if (!is_zero_ether_addr(key->dst))
-                       if (is_valid_ether_addr(key->dst) ||
-                           is_multicast_ether_addr(key->dst)) {
+               if (!is_zero_ether_addr(match.key->dst))
+                       if (is_valid_ether_addr(match.key->dst) ||
+                           is_multicast_ether_addr(match.key->dst)) {
                                /* set the mask if a valid dst_mac address */
                                for (i = 0; i < ETH_ALEN; i++)
                                        vf->mask.tcp_spec.dst_mac[i] |= 0xff;
                                ether_addr_copy(vf->data.tcp_spec.dst_mac,
-                                               key->dst);
+                                               match.key->dst);
                        }
 
-               if (!is_zero_ether_addr(key->src))
-                       if (is_valid_ether_addr(key->src) ||
-                           is_multicast_ether_addr(key->src)) {
+               if (!is_zero_ether_addr(match.key->src))
+                       if (is_valid_ether_addr(match.key->src) ||
+                           is_multicast_ether_addr(match.key->src)) {
                                /* set the mask if a valid dst_mac address */
                                for (i = 0; i < ETH_ALEN; i++)
                                        vf->mask.tcp_spec.src_mac[i] |= 0xff;
                                ether_addr_copy(vf->data.tcp_spec.src_mac,
-                                               key->src);
+                                               match.key->src);
                }
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
-               struct flow_dissector_key_vlan *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_VLAN,
-                                                 f->key);
-               struct flow_dissector_key_vlan *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_VLAN,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+               struct flow_match_vlan match;
 
-               if (mask->vlan_id) {
-                       if (mask->vlan_id == VLAN_VID_MASK) {
+               flow_rule_match_vlan(rule, &match);
+               if (match.mask->vlan_id) {
+                       if (match.mask->vlan_id == VLAN_VID_MASK) {
                                field_flags |= IAVF_CLOUD_FIELD_IVLAN;
                        } else {
                                dev_err(&adapter->pdev->dev, "Bad vlan mask %u\n",
-                                       mask->vlan_id);
+                                       match.mask->vlan_id);
                                return I40E_ERR_CONFIG;
                        }
                }
                vf->mask.tcp_spec.vlan_id |= cpu_to_be16(0xffff);
-               vf->data.tcp_spec.vlan_id = cpu_to_be16(key->vlan_id);
+               vf->data.tcp_spec.vlan_id = cpu_to_be16(match.key->vlan_id);
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
-               struct flow_dissector_key_control *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_CONTROL,
-                                                 f->key);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+               struct flow_match_control match;
 
-               addr_type = key->addr_type;
+               flow_rule_match_control(rule, &match);
+               addr_type = match.key->addr_type;
        }
 
        if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
-               struct flow_dissector_key_ipv4_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                                 f->key);
-               struct flow_dissector_key_ipv4_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                                 f->mask);
-
-               if (mask->dst) {
-                       if (mask->dst == cpu_to_be32(0xffffffff)) {
+               struct flow_match_ipv4_addrs match;
+
+               flow_rule_match_ipv4_addrs(rule, &match);
+               if (match.mask->dst) {
+                       if (match.mask->dst == cpu_to_be32(0xffffffff)) {
                                field_flags |= IAVF_CLOUD_FIELD_IIP;
                        } else {
                                dev_err(&adapter->pdev->dev, "Bad ip dst mask 0x%08x\n",
-                                       be32_to_cpu(mask->dst));
+                                       be32_to_cpu(match.mask->dst));
                                return I40E_ERR_CONFIG;
                        }
                }
 
-               if (mask->src) {
-                       if (mask->src == cpu_to_be32(0xffffffff)) {
+               if (match.mask->src) {
+                       if (match.mask->src == cpu_to_be32(0xffffffff)) {
                                field_flags |= IAVF_CLOUD_FIELD_IIP;
                        } else {
                                dev_err(&adapter->pdev->dev, "Bad ip src mask 0x%08x\n",
-                                       be32_to_cpu(mask->dst));
+                                       be32_to_cpu(match.mask->dst));
                                return I40E_ERR_CONFIG;
                        }
                }
@@ -2620,28 +2595,23 @@ static int iavf_parse_cls_flower(struct iavf_adapter *adapter,
                        dev_info(&adapter->pdev->dev, "Tenant id not allowed for ip filter\n");
                        return I40E_ERR_CONFIG;
                }
-               if (key->dst) {
+               if (match.key->dst) {
                        vf->mask.tcp_spec.dst_ip[0] |= cpu_to_be32(0xffffffff);
-                       vf->data.tcp_spec.dst_ip[0] = key->dst;
+                       vf->data.tcp_spec.dst_ip[0] = match.key->dst;
                }
-               if (key->src) {
+               if (match.key->src) {
                        vf->mask.tcp_spec.src_ip[0] |= cpu_to_be32(0xffffffff);
-                       vf->data.tcp_spec.src_ip[0] = key->src;
+                       vf->data.tcp_spec.src_ip[0] = match.key->src;
                }
        }
 
        if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
-               struct flow_dissector_key_ipv6_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                                 f->key);
-               struct flow_dissector_key_ipv6_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                                 f->mask);
+               struct flow_match_ipv6_addrs match;
+
+               flow_rule_match_ipv6_addrs(rule, &match);
 
                /* validate mask, make sure it is not IPV6_ADDR_ANY */
-               if (ipv6_addr_any(&mask->dst)) {
+               if (ipv6_addr_any(&match.mask->dst)) {
                        dev_err(&adapter->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
                                IPV6_ADDR_ANY);
                        return I40E_ERR_CONFIG;
@@ -2650,61 +2620,56 @@ static int iavf_parse_cls_flower(struct iavf_adapter *adapter,
                /* src and dest IPv6 address should not be LOOPBACK
                 * (0:0:0:0:0:0:0:1) which can be represented as ::1
                 */
-               if (ipv6_addr_loopback(&key->dst) ||
-                   ipv6_addr_loopback(&key->src)) {
+               if (ipv6_addr_loopback(&match.key->dst) ||
+                   ipv6_addr_loopback(&match.key->src)) {
                        dev_err(&adapter->pdev->dev,
                                "ipv6 addr should not be loopback\n");
                        return I40E_ERR_CONFIG;
                }
-               if (!ipv6_addr_any(&mask->dst) || !ipv6_addr_any(&mask->src))
+               if (!ipv6_addr_any(&match.mask->dst) ||
+                   !ipv6_addr_any(&match.mask->src))
                        field_flags |= IAVF_CLOUD_FIELD_IIP;
 
                for (i = 0; i < 4; i++)
                        vf->mask.tcp_spec.dst_ip[i] |= cpu_to_be32(0xffffffff);
-               memcpy(&vf->data.tcp_spec.dst_ip, &key->dst.s6_addr32,
+               memcpy(&vf->data.tcp_spec.dst_ip, &match.key->dst.s6_addr32,
                       sizeof(vf->data.tcp_spec.dst_ip));
                for (i = 0; i < 4; i++)
                        vf->mask.tcp_spec.src_ip[i] |= cpu_to_be32(0xffffffff);
-               memcpy(&vf->data.tcp_spec.src_ip, &key->src.s6_addr32,
+               memcpy(&vf->data.tcp_spec.src_ip, &match.key->src.s6_addr32,
                       sizeof(vf->data.tcp_spec.src_ip));
        }
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
-               struct flow_dissector_key_ports *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_PORTS,
-                                                 f->key);
-               struct flow_dissector_key_ports *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_PORTS,
-                                                 f->mask);
-
-               if (mask->src) {
-                       if (mask->src == cpu_to_be16(0xffff)) {
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+               struct flow_match_ports match;
+
+               flow_rule_match_ports(rule, &match);
+               if (match.mask->src) {
+                       if (match.mask->src == cpu_to_be16(0xffff)) {
                                field_flags |= IAVF_CLOUD_FIELD_IIP;
                        } else {
                                dev_err(&adapter->pdev->dev, "Bad src port mask %u\n",
-                                       be16_to_cpu(mask->src));
+                                       be16_to_cpu(match.mask->src));
                                return I40E_ERR_CONFIG;
                        }
                }
 
-               if (mask->dst) {
-                       if (mask->dst == cpu_to_be16(0xffff)) {
+               if (match.mask->dst) {
+                       if (match.mask->dst == cpu_to_be16(0xffff)) {
                                field_flags |= IAVF_CLOUD_FIELD_IIP;
                        } else {
                                dev_err(&adapter->pdev->dev, "Bad dst port mask %u\n",
-                                       be16_to_cpu(mask->dst));
+                                       be16_to_cpu(match.mask->dst));
                                return I40E_ERR_CONFIG;
                        }
                }
-               if (key->dst) {
+               if (match.key->dst) {
                        vf->mask.tcp_spec.dst_port |= cpu_to_be16(0xffff);
-                       vf->data.tcp_spec.dst_port = key->dst;
+                       vf->data.tcp_spec.dst_port = match.key->dst;
                }
 
-               if (key->src) {
+               if (match.key->src) {
                        vf->mask.tcp_spec.src_port |= cpu_to_be16(0xffff);
-                       vf->data.tcp_spec.src_port = key->src;
+                       vf->data.tcp_spec.src_port = match.key->src;
                }
        }
        vf->field_flags = field_flags;
index dfa357b..69b230c 100644 (file)
@@ -39,7 +39,7 @@
 #include "igb.h"
 
 #define MAJ 5
-#define MIN 4
+#define MIN 6
 #define BUILD 0
 #define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) "." \
 __stringify(BUILD) "-k"
@@ -1189,15 +1189,15 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter,
 {
        struct igb_q_vector *q_vector;
        struct igb_ring *ring;
-       int ring_count, size;
+       int ring_count;
+       size_t size;
 
        /* igb only supports 1 Tx and/or 1 Rx queue per vector */
        if (txr_count > 1 || rxr_count > 1)
                return -ENOMEM;
 
        ring_count = txr_count + rxr_count;
-       size = sizeof(struct igb_q_vector) +
-              (sizeof(struct igb_ring) * ring_count);
+       size = struct_size(q_vector, ring, ring_count);
 
        /* allocate q_vector and rings */
        q_vector = adapter->q_vector[v_idx];
@@ -2581,9 +2581,11 @@ static int igb_parse_cls_flower(struct igb_adapter *adapter,
                                int traffic_class,
                                struct igb_nfc_filter *input)
 {
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
+       struct flow_dissector *dissector = rule->match.dissector;
        struct netlink_ext_ack *extack = f->common.extack;
 
-       if (f->dissector->used_keys &
+       if (dissector->used_keys &
            ~(BIT(FLOW_DISSECTOR_KEY_BASIC) |
              BIT(FLOW_DISSECTOR_KEY_CONTROL) |
              BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
@@ -2593,78 +2595,60 @@ static int igb_parse_cls_flower(struct igb_adapter *adapter,
                return -EOPNOTSUPP;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
-               struct flow_dissector_key_eth_addrs *key, *mask;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct flow_match_eth_addrs match;
 
-               key = skb_flow_dissector_target(f->dissector,
-                                               FLOW_DISSECTOR_KEY_ETH_ADDRS,
-                                               f->key);
-               mask = skb_flow_dissector_target(f->dissector,
-                                                FLOW_DISSECTOR_KEY_ETH_ADDRS,
-                                                f->mask);
-
-               if (!is_zero_ether_addr(mask->dst)) {
-                       if (!is_broadcast_ether_addr(mask->dst)) {
+               flow_rule_match_eth_addrs(rule, &match);
+               if (!is_zero_ether_addr(match.mask->dst)) {
+                       if (!is_broadcast_ether_addr(match.mask->dst)) {
                                NL_SET_ERR_MSG_MOD(extack, "Only full masks are supported for destination MAC address");
                                return -EINVAL;
                        }
 
                        input->filter.match_flags |=
                                IGB_FILTER_FLAG_DST_MAC_ADDR;
-                       ether_addr_copy(input->filter.dst_addr, key->dst);
+                       ether_addr_copy(input->filter.dst_addr, match.key->dst);
                }
 
-               if (!is_zero_ether_addr(mask->src)) {
-                       if (!is_broadcast_ether_addr(mask->src)) {
+               if (!is_zero_ether_addr(match.mask->src)) {
+                       if (!is_broadcast_ether_addr(match.mask->src)) {
                                NL_SET_ERR_MSG_MOD(extack, "Only full masks are supported for source MAC address");
                                return -EINVAL;
                        }
 
                        input->filter.match_flags |=
                                IGB_FILTER_FLAG_SRC_MAC_ADDR;
-                       ether_addr_copy(input->filter.src_addr, key->src);
+                       ether_addr_copy(input->filter.src_addr, match.key->src);
                }
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *key, *mask;
-
-               key = skb_flow_dissector_target(f->dissector,
-                                               FLOW_DISSECTOR_KEY_BASIC,
-                                               f->key);
-               mask = skb_flow_dissector_target(f->dissector,
-                                                FLOW_DISSECTOR_KEY_BASIC,
-                                                f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
 
-               if (mask->n_proto) {
-                       if (mask->n_proto != ETHER_TYPE_FULL_MASK) {
+               flow_rule_match_basic(rule, &match);
+               if (match.mask->n_proto) {
+                       if (match.mask->n_proto != ETHER_TYPE_FULL_MASK) {
                                NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for EtherType filter");
                                return -EINVAL;
                        }
 
                        input->filter.match_flags |= IGB_FILTER_FLAG_ETHER_TYPE;
-                       input->filter.etype = key->n_proto;
+                       input->filter.etype = match.key->n_proto;
                }
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
-               struct flow_dissector_key_vlan *key, *mask;
-
-               key = skb_flow_dissector_target(f->dissector,
-                                               FLOW_DISSECTOR_KEY_VLAN,
-                                               f->key);
-               mask = skb_flow_dissector_target(f->dissector,
-                                                FLOW_DISSECTOR_KEY_VLAN,
-                                                f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+               struct flow_match_vlan match;
 
-               if (mask->vlan_priority) {
-                       if (mask->vlan_priority != VLAN_PRIO_FULL_MASK) {
+               flow_rule_match_vlan(rule, &match);
+               if (match.mask->vlan_priority) {
+                       if (match.mask->vlan_priority != VLAN_PRIO_FULL_MASK) {
                                NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for VLAN priority");
                                return -EINVAL;
                        }
 
                        input->filter.match_flags |= IGB_FILTER_FLAG_VLAN_TCI;
-                       input->filter.vlan_tci = key->vlan_priority;
+                       input->filter.vlan_tci = match.key->vlan_priority;
                }
        }
 
index 4387f6b..88c6f88 100644 (file)
@@ -7,4 +7,5 @@
 
 obj-$(CONFIG_IGC) += igc.o
 
-igc-objs := igc_main.o igc_mac.o igc_i225.o igc_base.o igc_nvm.o igc_phy.o
+igc-objs := igc_main.o igc_mac.o igc_i225.o igc_base.o igc_nvm.o igc_phy.o \
+igc_ethtool.o
index b1039dd..80faccc 100644 (file)
 
 #include "igc_hw.h"
 
-/* main */
+/* forward declaration */
+void igc_set_ethtool_ops(struct net_device *);
+
+struct igc_adapter;
+struct igc_ring;
+
+void igc_up(struct igc_adapter *adapter);
+void igc_down(struct igc_adapter *adapter);
+int igc_setup_tx_resources(struct igc_ring *ring);
+int igc_setup_rx_resources(struct igc_ring *ring);
+void igc_free_tx_resources(struct igc_ring *ring);
+void igc_free_rx_resources(struct igc_ring *ring);
+unsigned int igc_get_max_rss_queues(struct igc_adapter *adapter);
+void igc_set_flag_queue_pairs(struct igc_adapter *adapter,
+                             const u32 max_rss_queues);
+int igc_reinit_queues(struct igc_adapter *adapter);
+bool igc_has_link(struct igc_adapter *adapter);
+void igc_reset(struct igc_adapter *adapter);
+int igc_set_spd_dplx(struct igc_adapter *adapter, u32 spd, u8 dplx);
+
 extern char igc_driver_name[];
 extern char igc_driver_version[];
 
+#define IGC_REGS_LEN                   740
+#define IGC_RETA_SIZE                  128
+
 /* Interrupt defines */
 #define IGC_START_ITR                  648 /* ~6000 ints/sec */
 #define IGC_FLAG_HAS_MSI               BIT(0)
-#define IGC_FLAG_QUEUE_PAIRS           BIT(4)
+#define IGC_FLAG_QUEUE_PAIRS           BIT(3)
+#define IGC_FLAG_DMAC                  BIT(4)
 #define IGC_FLAG_NEED_LINK_UPDATE      BIT(9)
 #define IGC_FLAG_MEDIA_RESET           BIT(10)
 #define IGC_FLAG_MAS_ENABLE            BIT(12)
 #define IGC_FLAG_HAS_MSIX              BIT(13)
 #define IGC_FLAG_VLAN_PROMISC          BIT(15)
+#define IGC_FLAG_RX_LEGACY             BIT(16)
 
 #define IGC_START_ITR                  648 /* ~6000 ints/sec */
 #define IGC_4K_ITR                     980
@@ -60,6 +84,7 @@ extern char igc_driver_version[];
 #define IGC_RXBUFFER_2048              2048
 #define IGC_RXBUFFER_3072              3072
 
+#define AUTO_ALL_MODES         0
 #define IGC_RX_HDR_LEN                 IGC_RXBUFFER_256
 
 /* RX and TX descriptor control thresholds.
@@ -340,6 +365,8 @@ struct igc_adapter {
 
        struct igc_mac_addr *mac_table;
 
+       u8 rss_indir_tbl[IGC_RETA_SIZE];
+
        unsigned long link_check_timeout;
        struct igc_info ei;
 };
@@ -418,6 +445,9 @@ static inline s32 igc_read_phy_reg(struct igc_hw *hw, u32 offset, u16 *data)
        return 0;
 }
 
+/* forward declaration */
+void igc_reinit_locked(struct igc_adapter *);
+
 #define igc_rx_pg_size(_ring) (PAGE_SIZE << igc_rx_pg_order(_ring))
 
 #define IGC_TXD_DCMD   (IGC_ADVTXD_DCMD_EOP | IGC_ADVTXD_DCMD_RS)
index df40af7..51a8b87 100644 (file)
@@ -54,22 +54,6 @@ out:
 }
 
 /**
- * igc_check_for_link_base - Check for link
- * @hw: pointer to the HW structure
- *
- * If sgmii is enabled, then use the pcs register to determine link, otherwise
- * use the generic interface for determining link.
- */
-static s32 igc_check_for_link_base(struct igc_hw *hw)
-{
-       s32 ret_val = 0;
-
-       ret_val = igc_check_for_copper_link(hw);
-
-       return ret_val;
-}
-
-/**
  * igc_reset_hw_base - Reset hardware
  * @hw: pointer to the HW structure
  *
@@ -124,22 +108,6 @@ static s32 igc_reset_hw_base(struct igc_hw *hw)
 }
 
 /**
- * igc_get_phy_id_base - Retrieve PHY addr and id
- * @hw: pointer to the HW structure
- *
- * Retrieves the PHY address and ID for both PHY's which do and do not use
- * sgmi interface.
- */
-static s32 igc_get_phy_id_base(struct igc_hw *hw)
-{
-       s32  ret_val = 0;
-
-       ret_val = igc_get_phy_id(hw);
-
-       return ret_val;
-}
-
-/**
  * igc_init_nvm_params_base - Init NVM func ptrs.
  * @hw: pointer to the HW structure
  */
@@ -163,6 +131,7 @@ static s32 igc_init_nvm_params_base(struct igc_hw *hw)
        if (size > 15)
                size = 15;
 
+       nvm->type = igc_nvm_eeprom_spi;
        nvm->word_size = BIT(size);
        nvm->opcode_bits = 8;
        nvm->delay_usec = 1;
@@ -261,11 +230,11 @@ static s32 igc_init_phy_params_base(struct igc_hw *hw)
                goto out;
        }
 
-       ret_val = igc_get_phy_id_base(hw);
+       ret_val = igc_get_phy_id(hw);
        if (ret_val)
                return ret_val;
 
-       igc_check_for_link_base(hw);
+       igc_check_for_copper_link(hw);
 
        /* Verify phy id and set remaining function pointers */
        switch (phy->id) {
@@ -350,26 +319,6 @@ static void igc_release_phy_base(struct igc_hw *hw)
 }
 
 /**
- * igc_get_link_up_info_base - Get link speed/duplex info
- * @hw: pointer to the HW structure
- * @speed: stores the current speed
- * @duplex: stores the current duplex
- *
- * This is a wrapper function, if using the serial gigabit media independent
- * interface, use PCS to retrieve the link speed and duplex information.
- * Otherwise, use the generic function to get the link speed and duplex info.
- */
-static s32 igc_get_link_up_info_base(struct igc_hw *hw, u16 *speed,
-                                    u16 *duplex)
-{
-       s32 ret_val;
-
-       ret_val = igc_get_speed_and_duplex_copper(hw, speed, duplex);
-
-       return ret_val;
-}
-
-/**
  * igc_init_hw_base - Initialize hardware
  * @hw: pointer to the HW structure
  *
@@ -408,19 +357,6 @@ static s32 igc_init_hw_base(struct igc_hw *hw)
 }
 
 /**
- * igc_read_mac_addr_base - Read device MAC address
- * @hw: pointer to the HW structure
- */
-static s32 igc_read_mac_addr_base(struct igc_hw *hw)
-{
-       s32 ret_val = 0;
-
-       ret_val = igc_read_mac_addr(hw);
-
-       return ret_val;
-}
-
-/**
  * igc_power_down_phy_copper_base - Remove link during PHY power down
  * @hw: pointer to the HW structure
  *
@@ -512,10 +448,10 @@ void igc_rx_fifo_flush_base(struct igc_hw *hw)
 
 static struct igc_mac_operations igc_mac_ops_base = {
        .init_hw                = igc_init_hw_base,
-       .check_for_link         = igc_check_for_link_base,
+       .check_for_link         = igc_check_for_copper_link,
        .rar_set                = igc_rar_set,
-       .read_mac_addr          = igc_read_mac_addr_base,
-       .get_speed_and_duplex   = igc_get_link_up_info_base,
+       .read_mac_addr          = igc_read_mac_addr,
+       .get_speed_and_duplex   = igc_get_speed_and_duplex_copper,
 };
 
 static const struct igc_phy_operations igc_phy_ops_base = {
index 35588fa..76d4991 100644 (file)
@@ -36,28 +36,6 @@ union igc_adv_tx_desc {
 
 #define IGC_RAR_ENTRIES                16
 
-struct igc_adv_data_desc {
-       __le64 buffer_addr;    /* Address of the descriptor's data buffer */
-       union {
-               u32 data;
-               struct {
-                       u32 datalen:16; /* Data buffer length */
-                       u32 rsvd:4;
-                       u32 dtyp:4;  /* Descriptor type */
-                       u32 dcmd:8;  /* Descriptor command */
-               } config;
-       } lower;
-       union {
-               u32 data;
-               struct {
-                       u32 status:4;  /* Descriptor status */
-                       u32 idx:4;
-                       u32 popts:6;  /* Packet Options */
-                       u32 paylen:18; /* Payload length */
-               } options;
-       } upper;
-};
-
 /* Receive Descriptor - Advanced */
 union igc_adv_rx_desc {
        struct {
@@ -90,9 +68,6 @@ union igc_adv_rx_desc {
        } wb;  /* writeback */
 };
 
-/* Adv Transmit Descriptor Config Masks */
-#define IGC_ADVTXD_PAYLEN_SHIFT        14 /* Adv desc PAYLEN shift */
-
 /* Additional Transmit Descriptor Control definitions */
 #define IGC_TXDCTL_QUEUE_ENABLE        0x02000000 /* Ena specific Tx Queue */
 
index 8740754..7d1bdcd 100644 (file)
@@ -4,6 +4,10 @@
 #ifndef _IGC_DEFINES_H_
 #define _IGC_DEFINES_H_
 
+/* Number of Transmit and Receive Descriptors must be a multiple of 8 */
+#define REQ_TX_DESCRIPTOR_MULTIPLE  8
+#define REQ_RX_DESCRIPTOR_MULTIPLE  8
+
 #define IGC_CTRL_EXT_DRV_LOAD  0x10000000 /* Drv loaded bit for FW */
 
 /* PCI Bus Info */
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
new file mode 100644 (file)
index 0000000..eff37a6
--- /dev/null
@@ -0,0 +1,1032 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c)  2018 Intel Corporation */
+
+/* ethtool support for igc */
+#include <linux/pm_runtime.h>
+
+#include "igc.h"
+
+static const char igc_priv_flags_strings[][ETH_GSTRING_LEN] = {
+#define IGC_PRIV_FLAGS_LEGACY_RX       BIT(0)
+       "legacy-rx",
+};
+
+#define IGC_PRIV_FLAGS_STR_LEN ARRAY_SIZE(igc_priv_flags_strings)
+
+static void igc_get_drvinfo(struct net_device *netdev,
+                           struct ethtool_drvinfo *drvinfo)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+
+       strlcpy(drvinfo->driver,  igc_driver_name, sizeof(drvinfo->driver));
+       strlcpy(drvinfo->version, igc_driver_version, sizeof(drvinfo->version));
+
+       /* add fw_version here */
+       strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
+               sizeof(drvinfo->bus_info));
+
+       drvinfo->n_priv_flags = IGC_PRIV_FLAGS_STR_LEN;
+}
+
+static int igc_get_regs_len(struct net_device *netdev)
+{
+       return IGC_REGS_LEN * sizeof(u32);
+}
+
+static void igc_get_regs(struct net_device *netdev,
+                        struct ethtool_regs *regs, void *p)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       struct igc_hw *hw = &adapter->hw;
+       u32 *regs_buff = p;
+       u8 i;
+
+       memset(p, 0, IGC_REGS_LEN * sizeof(u32));
+
+       regs->version = (1u << 24) | (hw->revision_id << 16) | hw->device_id;
+
+       /* General Registers */
+       regs_buff[0] = rd32(IGC_CTRL);
+       regs_buff[1] = rd32(IGC_STATUS);
+       regs_buff[2] = rd32(IGC_CTRL_EXT);
+       regs_buff[3] = rd32(IGC_MDIC);
+       regs_buff[4] = rd32(IGC_CONNSW);
+
+       /* NVM Register */
+       regs_buff[5] = rd32(IGC_EECD);
+
+       /* Interrupt */
+       /* Reading EICS for EICR because they read the
+        * same but EICS does not clear on read
+        */
+       regs_buff[6] = rd32(IGC_EICS);
+       regs_buff[7] = rd32(IGC_EICS);
+       regs_buff[8] = rd32(IGC_EIMS);
+       regs_buff[9] = rd32(IGC_EIMC);
+       regs_buff[10] = rd32(IGC_EIAC);
+       regs_buff[11] = rd32(IGC_EIAM);
+       /* Reading ICS for ICR because they read the
+        * same but ICS does not clear on read
+        */
+       regs_buff[12] = rd32(IGC_ICS);
+       regs_buff[13] = rd32(IGC_ICS);
+       regs_buff[14] = rd32(IGC_IMS);
+       regs_buff[15] = rd32(IGC_IMC);
+       regs_buff[16] = rd32(IGC_IAC);
+       regs_buff[17] = rd32(IGC_IAM);
+
+       /* Flow Control */
+       regs_buff[18] = rd32(IGC_FCAL);
+       regs_buff[19] = rd32(IGC_FCAH);
+       regs_buff[20] = rd32(IGC_FCTTV);
+       regs_buff[21] = rd32(IGC_FCRTL);
+       regs_buff[22] = rd32(IGC_FCRTH);
+       regs_buff[23] = rd32(IGC_FCRTV);
+
+       /* Receive */
+       regs_buff[24] = rd32(IGC_RCTL);
+       regs_buff[25] = rd32(IGC_RXCSUM);
+       regs_buff[26] = rd32(IGC_RLPML);
+       regs_buff[27] = rd32(IGC_RFCTL);
+
+       /* Transmit */
+       regs_buff[28] = rd32(IGC_TCTL);
+       regs_buff[29] = rd32(IGC_TIPG);
+
+       /* Wake Up */
+
+       /* MAC */
+
+       /* Statistics */
+       regs_buff[30] = adapter->stats.crcerrs;
+       regs_buff[31] = adapter->stats.algnerrc;
+       regs_buff[32] = adapter->stats.symerrs;
+       regs_buff[33] = adapter->stats.rxerrc;
+       regs_buff[34] = adapter->stats.mpc;
+       regs_buff[35] = adapter->stats.scc;
+       regs_buff[36] = adapter->stats.ecol;
+       regs_buff[37] = adapter->stats.mcc;
+       regs_buff[38] = adapter->stats.latecol;
+       regs_buff[39] = adapter->stats.colc;
+       regs_buff[40] = adapter->stats.dc;
+       regs_buff[41] = adapter->stats.tncrs;
+       regs_buff[42] = adapter->stats.sec;
+       regs_buff[43] = adapter->stats.htdpmc;
+       regs_buff[44] = adapter->stats.rlec;
+       regs_buff[45] = adapter->stats.xonrxc;
+       regs_buff[46] = adapter->stats.xontxc;
+       regs_buff[47] = adapter->stats.xoffrxc;
+       regs_buff[48] = adapter->stats.xofftxc;
+       regs_buff[49] = adapter->stats.fcruc;
+       regs_buff[50] = adapter->stats.prc64;
+       regs_buff[51] = adapter->stats.prc127;
+       regs_buff[52] = adapter->stats.prc255;
+       regs_buff[53] = adapter->stats.prc511;
+       regs_buff[54] = adapter->stats.prc1023;
+       regs_buff[55] = adapter->stats.prc1522;
+       regs_buff[56] = adapter->stats.gprc;
+       regs_buff[57] = adapter->stats.bprc;
+       regs_buff[58] = adapter->stats.mprc;
+       regs_buff[59] = adapter->stats.gptc;
+       regs_buff[60] = adapter->stats.gorc;
+       regs_buff[61] = adapter->stats.gotc;
+       regs_buff[62] = adapter->stats.rnbc;
+       regs_buff[63] = adapter->stats.ruc;
+       regs_buff[64] = adapter->stats.rfc;
+       regs_buff[65] = adapter->stats.roc;
+       regs_buff[66] = adapter->stats.rjc;
+       regs_buff[67] = adapter->stats.mgprc;
+       regs_buff[68] = adapter->stats.mgpdc;
+       regs_buff[69] = adapter->stats.mgptc;
+       regs_buff[70] = adapter->stats.tor;
+       regs_buff[71] = adapter->stats.tot;
+       regs_buff[72] = adapter->stats.tpr;
+       regs_buff[73] = adapter->stats.tpt;
+       regs_buff[74] = adapter->stats.ptc64;
+       regs_buff[75] = adapter->stats.ptc127;
+       regs_buff[76] = adapter->stats.ptc255;
+       regs_buff[77] = adapter->stats.ptc511;
+       regs_buff[78] = adapter->stats.ptc1023;
+       regs_buff[79] = adapter->stats.ptc1522;
+       regs_buff[80] = adapter->stats.mptc;
+       regs_buff[81] = adapter->stats.bptc;
+       regs_buff[82] = adapter->stats.tsctc;
+       regs_buff[83] = adapter->stats.iac;
+       regs_buff[84] = adapter->stats.rpthc;
+       regs_buff[85] = adapter->stats.hgptc;
+       regs_buff[86] = adapter->stats.hgorc;
+       regs_buff[87] = adapter->stats.hgotc;
+       regs_buff[88] = adapter->stats.lenerrs;
+       regs_buff[89] = adapter->stats.scvpc;
+       regs_buff[90] = adapter->stats.hrmpc;
+
+       for (i = 0; i < 4; i++)
+               regs_buff[91 + i] = rd32(IGC_SRRCTL(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[95 + i] = rd32(IGC_PSRTYPE(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[99 + i] = rd32(IGC_RDBAL(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[103 + i] = rd32(IGC_RDBAH(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[107 + i] = rd32(IGC_RDLEN(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[111 + i] = rd32(IGC_RDH(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[115 + i] = rd32(IGC_RDT(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[119 + i] = rd32(IGC_RXDCTL(i));
+
+       for (i = 0; i < 10; i++)
+               regs_buff[123 + i] = rd32(IGC_EITR(i));
+       for (i = 0; i < 16; i++)
+               regs_buff[139 + i] = rd32(IGC_RAL(i));
+       for (i = 0; i < 16; i++)
+               regs_buff[145 + i] = rd32(IGC_RAH(i));
+
+       for (i = 0; i < 4; i++)
+               regs_buff[149 + i] = rd32(IGC_TDBAL(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[152 + i] = rd32(IGC_TDBAH(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[156 + i] = rd32(IGC_TDLEN(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[160 + i] = rd32(IGC_TDH(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[164 + i] = rd32(IGC_TDT(i));
+       for (i = 0; i < 4; i++)
+               regs_buff[168 + i] = rd32(IGC_TXDCTL(i));
+}
+
+static u32 igc_get_msglevel(struct net_device *netdev)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+
+       return adapter->msg_enable;
+}
+
+static void igc_set_msglevel(struct net_device *netdev, u32 data)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+
+       adapter->msg_enable = data;
+}
+
+static int igc_nway_reset(struct net_device *netdev)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+
+       if (netif_running(netdev))
+               igc_reinit_locked(adapter);
+       return 0;
+}
+
+static u32 igc_get_link(struct net_device *netdev)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       struct igc_mac_info *mac = &adapter->hw.mac;
+
+       /* If the link is not reported up to netdev, interrupts are disabled,
+        * and so the physical link state may have changed since we last
+        * looked. Set get_link_status to make sure that the true link
+        * state is interrogated, rather than pulling a cached and possibly
+        * stale link state from the driver.
+        */
+       if (!netif_carrier_ok(netdev))
+               mac->get_link_status = 1;
+
+       return igc_has_link(adapter);
+}
+
+static int igc_get_eeprom_len(struct net_device *netdev)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+
+       return adapter->hw.nvm.word_size * 2;
+}
+
+static int igc_get_eeprom(struct net_device *netdev,
+                         struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       struct igc_hw *hw = &adapter->hw;
+       int first_word, last_word;
+       u16 *eeprom_buff;
+       int ret_val = 0;
+       u16 i;
+
+       if (eeprom->len == 0)
+               return -EINVAL;
+
+       eeprom->magic = hw->vendor_id | (hw->device_id << 16);
+
+       first_word = eeprom->offset >> 1;
+       last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+
+       eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
+                                   GFP_KERNEL);
+       if (!eeprom_buff)
+               return -ENOMEM;
+
+       if (hw->nvm.type == igc_nvm_eeprom_spi) {
+               ret_val = hw->nvm.ops.read(hw, first_word,
+                                          last_word - first_word + 1,
+                                          eeprom_buff);
+       } else {
+               for (i = 0; i < last_word - first_word + 1; i++) {
+                       ret_val = hw->nvm.ops.read(hw, first_word + i, 1,
+                                                  &eeprom_buff[i]);
+                       if (ret_val)
+                               break;
+               }
+       }
+
+       /* Device's eeprom is always little-endian, word addressable */
+       for (i = 0; i < last_word - first_word + 1; i++)
+               le16_to_cpus(&eeprom_buff[i]);
+
+       memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1),
+              eeprom->len);
+       kfree(eeprom_buff);
+
+       return ret_val;
+}
+
+static int igc_set_eeprom(struct net_device *netdev,
+                         struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       struct igc_hw *hw = &adapter->hw;
+       int max_len, first_word, last_word, ret_val = 0;
+       u16 *eeprom_buff;
+       void *ptr;
+       u16 i;
+
+       if (eeprom->len == 0)
+               return -EOPNOTSUPP;
+
+       if (hw->mac.type >= igc_i225 &&
+           !igc_get_flash_presence_i225(hw)) {
+               return -EOPNOTSUPP;
+       }
+
+       if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
+               return -EFAULT;
+
+       max_len = hw->nvm.word_size * 2;
+
+       first_word = eeprom->offset >> 1;
+       last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+       eeprom_buff = kmalloc(max_len, GFP_KERNEL);
+       if (!eeprom_buff)
+               return -ENOMEM;
+
+       ptr = (void *)eeprom_buff;
+
+       if (eeprom->offset & 1) {
+               /* need read/modify/write of first changed EEPROM word
+                * only the second byte of the word is being modified
+                */
+               ret_val = hw->nvm.ops.read(hw, first_word, 1,
+                                           &eeprom_buff[0]);
+               ptr++;
+       }
+       if (((eeprom->offset + eeprom->len) & 1) && ret_val == 0) {
+               /* need read/modify/write of last changed EEPROM word
+                * only the first byte of the word is being modified
+                */
+               ret_val = hw->nvm.ops.read(hw, last_word, 1,
+                                  &eeprom_buff[last_word - first_word]);
+       }
+
+       /* Device's eeprom is always little-endian, word addressable */
+       for (i = 0; i < last_word - first_word + 1; i++)
+               le16_to_cpus(&eeprom_buff[i]);
+
+       memcpy(ptr, bytes, eeprom->len);
+
+       for (i = 0; i < last_word - first_word + 1; i++)
+               eeprom_buff[i] = cpu_to_le16(eeprom_buff[i]);
+
+       ret_val = hw->nvm.ops.write(hw, first_word,
+                                   last_word - first_word + 1, eeprom_buff);
+
+       /* Update the checksum if nvm write succeeded */
+       if (ret_val == 0)
+               hw->nvm.ops.update(hw);
+
+       /* check if need: igc_set_fw_version(adapter); */
+       kfree(eeprom_buff);
+       return ret_val;
+}
+
+static void igc_get_ringparam(struct net_device *netdev,
+                             struct ethtool_ringparam *ring)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+
+       ring->rx_max_pending = IGC_MAX_RXD;
+       ring->tx_max_pending = IGC_MAX_TXD;
+       ring->rx_pending = adapter->rx_ring_count;
+       ring->tx_pending = adapter->tx_ring_count;
+}
+
+static int igc_set_ringparam(struct net_device *netdev,
+                            struct ethtool_ringparam *ring)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       struct igc_ring *temp_ring;
+       u16 new_rx_count, new_tx_count;
+       int i, err = 0;
+
+       if (ring->rx_mini_pending || ring->rx_jumbo_pending)
+               return -EINVAL;
+
+       new_rx_count = min_t(u32, ring->rx_pending, IGC_MAX_RXD);
+       new_rx_count = max_t(u16, new_rx_count, IGC_MIN_RXD);
+       new_rx_count = ALIGN(new_rx_count, REQ_RX_DESCRIPTOR_MULTIPLE);
+
+       new_tx_count = min_t(u32, ring->tx_pending, IGC_MAX_TXD);
+       new_tx_count = max_t(u16, new_tx_count, IGC_MIN_TXD);
+       new_tx_count = ALIGN(new_tx_count, REQ_TX_DESCRIPTOR_MULTIPLE);
+
+       if (new_tx_count == adapter->tx_ring_count &&
+           new_rx_count == adapter->rx_ring_count) {
+               /* nothing to do */
+               return 0;
+       }
+
+       while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
+               usleep_range(1000, 2000);
+
+       if (!netif_running(adapter->netdev)) {
+               for (i = 0; i < adapter->num_tx_queues; i++)
+                       adapter->tx_ring[i]->count = new_tx_count;
+               for (i = 0; i < adapter->num_rx_queues; i++)
+                       adapter->rx_ring[i]->count = new_rx_count;
+               adapter->tx_ring_count = new_tx_count;
+               adapter->rx_ring_count = new_rx_count;
+               goto clear_reset;
+       }
+
+       if (adapter->num_tx_queues > adapter->num_rx_queues)
+               temp_ring = vmalloc(array_size(sizeof(struct igc_ring),
+                                              adapter->num_tx_queues));
+       else
+               temp_ring = vmalloc(array_size(sizeof(struct igc_ring),
+                                              adapter->num_rx_queues));
+
+       if (!temp_ring) {
+               err = -ENOMEM;
+               goto clear_reset;
+       }
+
+       igc_down(adapter);
+
+       /* We can't just free everything and then setup again,
+        * because the ISRs in MSI-X mode get passed pointers
+        * to the Tx and Rx ring structs.
+        */
+       if (new_tx_count != adapter->tx_ring_count) {
+               for (i = 0; i < adapter->num_tx_queues; i++) {
+                       memcpy(&temp_ring[i], adapter->tx_ring[i],
+                              sizeof(struct igc_ring));
+
+                       temp_ring[i].count = new_tx_count;
+                       err = igc_setup_tx_resources(&temp_ring[i]);
+                       if (err) {
+                               while (i) {
+                                       i--;
+                                       igc_free_tx_resources(&temp_ring[i]);
+                               }
+                               goto err_setup;
+                       }
+               }
+
+               for (i = 0; i < adapter->num_tx_queues; i++) {
+                       igc_free_tx_resources(adapter->tx_ring[i]);
+
+                       memcpy(adapter->tx_ring[i], &temp_ring[i],
+                              sizeof(struct igc_ring));
+               }
+
+               adapter->tx_ring_count = new_tx_count;
+       }
+
+       if (new_rx_count != adapter->rx_ring_count) {
+               for (i = 0; i < adapter->num_rx_queues; i++) {
+                       memcpy(&temp_ring[i], adapter->rx_ring[i],
+                              sizeof(struct igc_ring));
+
+                       temp_ring[i].count = new_rx_count;
+                       err = igc_setup_rx_resources(&temp_ring[i]);
+                       if (err) {
+                               while (i) {
+                                       i--;
+                                       igc_free_rx_resources(&temp_ring[i]);
+                               }
+                               goto err_setup;
+                       }
+               }
+
+               for (i = 0; i < adapter->num_rx_queues; i++) {
+                       igc_free_rx_resources(adapter->rx_ring[i]);
+
+                       memcpy(adapter->rx_ring[i], &temp_ring[i],
+                              sizeof(struct igc_ring));
+               }
+
+               adapter->rx_ring_count = new_rx_count;
+       }
+err_setup:
+       igc_up(adapter);
+       vfree(temp_ring);
+clear_reset:
+       clear_bit(__IGC_RESETTING, &adapter->state);
+       return err;
+}
+
+static void igc_get_pauseparam(struct net_device *netdev,
+                              struct ethtool_pauseparam *pause)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       struct igc_hw *hw = &adapter->hw;
+
+       pause->autoneg =
+               (adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
+
+       if (hw->fc.current_mode == igc_fc_rx_pause) {
+               pause->rx_pause = 1;
+       } else if (hw->fc.current_mode == igc_fc_tx_pause) {
+               pause->tx_pause = 1;
+       } else if (hw->fc.current_mode == igc_fc_full) {
+               pause->rx_pause = 1;
+               pause->tx_pause = 1;
+       }
+}
+
+static int igc_set_pauseparam(struct net_device *netdev,
+                             struct ethtool_pauseparam *pause)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       struct igc_hw *hw = &adapter->hw;
+       int retval = 0;
+
+       adapter->fc_autoneg = pause->autoneg;
+
+       while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
+               usleep_range(1000, 2000);
+
+       if (adapter->fc_autoneg == AUTONEG_ENABLE) {
+               hw->fc.requested_mode = igc_fc_default;
+               if (netif_running(adapter->netdev)) {
+                       igc_down(adapter);
+                       igc_up(adapter);
+               } else {
+                       igc_reset(adapter);
+               }
+       } else {
+               if (pause->rx_pause && pause->tx_pause)
+                       hw->fc.requested_mode = igc_fc_full;
+               else if (pause->rx_pause && !pause->tx_pause)
+                       hw->fc.requested_mode = igc_fc_rx_pause;
+               else if (!pause->rx_pause && pause->tx_pause)
+                       hw->fc.requested_mode = igc_fc_tx_pause;
+               else if (!pause->rx_pause && !pause->tx_pause)
+                       hw->fc.requested_mode = igc_fc_none;
+
+               hw->fc.current_mode = hw->fc.requested_mode;
+
+               retval = ((hw->phy.media_type == igc_media_type_copper) ?
+                         igc_force_mac_fc(hw) : igc_setup_link(hw));
+       }
+
+       clear_bit(__IGC_RESETTING, &adapter->state);
+       return retval;
+}
+
+static int igc_get_coalesce(struct net_device *netdev,
+                           struct ethtool_coalesce *ec)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+
+       if (adapter->rx_itr_setting <= 3)
+               ec->rx_coalesce_usecs = adapter->rx_itr_setting;
+       else
+               ec->rx_coalesce_usecs = adapter->rx_itr_setting >> 2;
+
+       if (!(adapter->flags & IGC_FLAG_QUEUE_PAIRS)) {
+               if (adapter->tx_itr_setting <= 3)
+                       ec->tx_coalesce_usecs = adapter->tx_itr_setting;
+               else
+                       ec->tx_coalesce_usecs = adapter->tx_itr_setting >> 2;
+       }
+
+       return 0;
+}
+
+static int igc_set_coalesce(struct net_device *netdev,
+                           struct ethtool_coalesce *ec)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       int i;
+
+       if (ec->rx_max_coalesced_frames ||
+           ec->rx_coalesce_usecs_irq ||
+           ec->rx_max_coalesced_frames_irq ||
+           ec->tx_max_coalesced_frames ||
+           ec->tx_coalesce_usecs_irq ||
+           ec->stats_block_coalesce_usecs ||
+           ec->use_adaptive_rx_coalesce ||
+           ec->use_adaptive_tx_coalesce ||
+           ec->pkt_rate_low ||
+           ec->rx_coalesce_usecs_low ||
+           ec->rx_max_coalesced_frames_low ||
+           ec->tx_coalesce_usecs_low ||
+           ec->tx_max_coalesced_frames_low ||
+           ec->pkt_rate_high ||
+           ec->rx_coalesce_usecs_high ||
+           ec->rx_max_coalesced_frames_high ||
+           ec->tx_coalesce_usecs_high ||
+           ec->tx_max_coalesced_frames_high ||
+           ec->rate_sample_interval)
+               return -ENOTSUPP;
+
+       if (ec->rx_coalesce_usecs > IGC_MAX_ITR_USECS ||
+           (ec->rx_coalesce_usecs > 3 &&
+            ec->rx_coalesce_usecs < IGC_MIN_ITR_USECS) ||
+           ec->rx_coalesce_usecs == 2)
+               return -EINVAL;
+
+       if (ec->tx_coalesce_usecs > IGC_MAX_ITR_USECS ||
+           (ec->tx_coalesce_usecs > 3 &&
+            ec->tx_coalesce_usecs < IGC_MIN_ITR_USECS) ||
+           ec->tx_coalesce_usecs == 2)
+               return -EINVAL;
+
+       if ((adapter->flags & IGC_FLAG_QUEUE_PAIRS) && ec->tx_coalesce_usecs)
+               return -EINVAL;
+
+       /* If ITR is disabled, disable DMAC */
+       if (ec->rx_coalesce_usecs == 0) {
+               if (adapter->flags & IGC_FLAG_DMAC)
+                       adapter->flags &= ~IGC_FLAG_DMAC;
+       }
+
+       /* convert to rate of irq's per second */
+       if (ec->rx_coalesce_usecs && ec->rx_coalesce_usecs <= 3)
+               adapter->rx_itr_setting = ec->rx_coalesce_usecs;
+       else
+               adapter->rx_itr_setting = ec->rx_coalesce_usecs << 2;
+
+       /* convert to rate of irq's per second */
+       if (adapter->flags & IGC_FLAG_QUEUE_PAIRS)
+               adapter->tx_itr_setting = adapter->rx_itr_setting;
+       else if (ec->tx_coalesce_usecs && ec->tx_coalesce_usecs <= 3)
+               adapter->tx_itr_setting = ec->tx_coalesce_usecs;
+       else
+               adapter->tx_itr_setting = ec->tx_coalesce_usecs << 2;
+
+       for (i = 0; i < adapter->num_q_vectors; i++) {
+               struct igc_q_vector *q_vector = adapter->q_vector[i];
+
+               q_vector->tx.work_limit = adapter->tx_work_limit;
+               if (q_vector->rx.ring)
+                       q_vector->itr_val = adapter->rx_itr_setting;
+               else
+                       q_vector->itr_val = adapter->tx_itr_setting;
+               if (q_vector->itr_val && q_vector->itr_val <= 3)
+                       q_vector->itr_val = IGC_START_ITR;
+               q_vector->set_itr = 1;
+       }
+
+       return 0;
+}
+
+void igc_write_rss_indir_tbl(struct igc_adapter *adapter)
+{
+       struct igc_hw *hw = &adapter->hw;
+       u32 reg = IGC_RETA(0);
+       u32 shift = 0;
+       int i = 0;
+
+       while (i < IGC_RETA_SIZE) {
+               u32 val = 0;
+               int j;
+
+               for (j = 3; j >= 0; j--) {
+                       val <<= 8;
+                       val |= adapter->rss_indir_tbl[i + j];
+               }
+
+               wr32(reg, val << shift);
+               reg += 4;
+               i += 4;
+       }
+}
+
+static u32 igc_get_rxfh_indir_size(struct net_device *netdev)
+{
+       return IGC_RETA_SIZE;
+}
+
+static int igc_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
+                       u8 *hfunc)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       int i;
+
+       if (hfunc)
+               *hfunc = ETH_RSS_HASH_TOP;
+       if (!indir)
+               return 0;
+       for (i = 0; i < IGC_RETA_SIZE; i++)
+               indir[i] = adapter->rss_indir_tbl[i];
+
+       return 0;
+}
+
+static int igc_set_rxfh(struct net_device *netdev, const u32 *indir,
+                       const u8 *key, const u8 hfunc)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       u32 num_queues;
+       int i;
+
+       /* We do not allow change in unsupported parameters */
+       if (key ||
+           (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
+               return -EOPNOTSUPP;
+       if (!indir)
+               return 0;
+
+       num_queues = adapter->rss_queues;
+
+       /* Verify user input. */
+       for (i = 0; i < IGC_RETA_SIZE; i++)
+               if (indir[i] >= num_queues)
+                       return -EINVAL;
+
+       for (i = 0; i < IGC_RETA_SIZE; i++)
+               adapter->rss_indir_tbl[i] = indir[i];
+
+       igc_write_rss_indir_tbl(adapter);
+
+       return 0;
+}
+
+static unsigned int igc_max_channels(struct igc_adapter *adapter)
+{
+       return igc_get_max_rss_queues(adapter);
+}
+
+static void igc_get_channels(struct net_device *netdev,
+                            struct ethtool_channels *ch)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+
+       /* Report maximum channels */
+       ch->max_combined = igc_max_channels(adapter);
+
+       /* Report info for other vector */
+       if (adapter->flags & IGC_FLAG_HAS_MSIX) {
+               ch->max_other = NON_Q_VECTORS;
+               ch->other_count = NON_Q_VECTORS;
+       }
+
+       ch->combined_count = adapter->rss_queues;
+}
+
+static int igc_set_channels(struct net_device *netdev,
+                           struct ethtool_channels *ch)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       unsigned int count = ch->combined_count;
+       unsigned int max_combined = 0;
+
+       /* Verify they are not requesting separate vectors */
+       if (!count || ch->rx_count || ch->tx_count)
+               return -EINVAL;
+
+       /* Verify other_count is valid and has not been changed */
+       if (ch->other_count != NON_Q_VECTORS)
+               return -EINVAL;
+
+       /* Verify the number of channels doesn't exceed hw limits */
+       max_combined = igc_max_channels(adapter);
+       if (count > max_combined)
+               return -EINVAL;
+
+       if (count != adapter->rss_queues) {
+               adapter->rss_queues = count;
+               igc_set_flag_queue_pairs(adapter, max_combined);
+
+               /* Hardware has to reinitialize queues and interrupts to
+                * match the new configuration.
+                */
+               return igc_reinit_queues(adapter);
+       }
+
+       return 0;
+}
+
+static u32 igc_get_priv_flags(struct net_device *netdev)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       u32 priv_flags = 0;
+
+       if (adapter->flags & IGC_FLAG_RX_LEGACY)
+               priv_flags |= IGC_PRIV_FLAGS_LEGACY_RX;
+
+       return priv_flags;
+}
+
+static int igc_set_priv_flags(struct net_device *netdev, u32 priv_flags)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       unsigned int flags = adapter->flags;
+
+       flags &= ~IGC_FLAG_RX_LEGACY;
+       if (priv_flags & IGC_PRIV_FLAGS_LEGACY_RX)
+               flags |= IGC_FLAG_RX_LEGACY;
+
+       if (flags != adapter->flags) {
+               adapter->flags = flags;
+
+               /* reset interface to repopulate queues */
+               if (netif_running(netdev))
+                       igc_reinit_locked(adapter);
+       }
+
+       return 0;
+}
+
+static int igc_ethtool_begin(struct net_device *netdev)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+
+       pm_runtime_get_sync(&adapter->pdev->dev);
+       return 0;
+}
+
+static void igc_ethtool_complete(struct net_device *netdev)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+
+       pm_runtime_put(&adapter->pdev->dev);
+}
+
+static int igc_get_link_ksettings(struct net_device *netdev,
+                                 struct ethtool_link_ksettings *cmd)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       struct igc_hw *hw = &adapter->hw;
+       u32 status;
+       u32 speed;
+
+       ethtool_link_ksettings_zero_link_mode(cmd, supported);
+       ethtool_link_ksettings_zero_link_mode(cmd, advertising);
+
+       /* supported link modes */
+       ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Half);
+       ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Full);
+       ethtool_link_ksettings_add_link_mode(cmd, supported, 100baseT_Half);
+       ethtool_link_ksettings_add_link_mode(cmd, supported, 100baseT_Full);
+       ethtool_link_ksettings_add_link_mode(cmd, supported, 1000baseT_Full);
+       ethtool_link_ksettings_add_link_mode(cmd, supported, 2500baseT_Full);
+
+       /* twisted pair */
+       cmd->base.port = PORT_TP;
+       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);
+
+       /* set autoneg settings */
+       if (hw->mac.autoneg == 1) {
+               ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    Autoneg);
+       }
+
+       switch (hw->fc.requested_mode) {
+       case igc_fc_full:
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
+               break;
+       case igc_fc_rx_pause:
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    Asym_Pause);
+               break;
+       case igc_fc_tx_pause:
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    Asym_Pause);
+               break;
+       default:
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    Asym_Pause);
+       }
+
+       status = rd32(IGC_STATUS);
+
+       if (status & IGC_STATUS_LU) {
+               if (status & IGC_STATUS_SPEED_1000) {
+                       /* For I225, STATUS will indicate 1G speed in both
+                        * 1 Gbps and 2.5 Gbps link modes.
+                        * An additional bit is used
+                        * to differentiate between 1 Gbps and 2.5 Gbps.
+                        */
+                       if (hw->mac.type == igc_i225 &&
+                           (status & IGC_STATUS_SPEED_2500)) {
+                               speed = SPEED_2500;
+                               hw_dbg("2500 Mbs, ");
+                       } else {
+                               speed = SPEED_1000;
+                               hw_dbg("1000 Mbs, ");
+                       }
+               } else if (status & IGC_STATUS_SPEED_100) {
+                       speed = SPEED_100;
+                       hw_dbg("100 Mbs, ");
+               } else {
+                       speed = SPEED_10;
+                       hw_dbg("10 Mbs, ");
+               }
+               if ((status & IGC_STATUS_FD) ||
+                   hw->phy.media_type != igc_media_type_copper)
+                       cmd->base.duplex = DUPLEX_FULL;
+               else
+                       cmd->base.duplex = DUPLEX_HALF;
+       } else {
+               speed = SPEED_UNKNOWN;
+               cmd->base.duplex = DUPLEX_UNKNOWN;
+       }
+       cmd->base.speed = speed;
+       if (hw->mac.autoneg)
+               cmd->base.autoneg = AUTONEG_ENABLE;
+       else
+               cmd->base.autoneg = AUTONEG_DISABLE;
+
+       /* MDI-X => 2; MDI =>1; Invalid =>0 */
+       if (hw->phy.media_type == igc_media_type_copper)
+               cmd->base.eth_tp_mdix = hw->phy.is_mdix ? ETH_TP_MDI_X :
+                                                     ETH_TP_MDI;
+       else
+               cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
+
+       if (hw->phy.mdix == AUTO_ALL_MODES)
+               cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO;
+       else
+               cmd->base.eth_tp_mdix_ctrl = hw->phy.mdix;
+
+       return 0;
+}
+
+static int igc_set_link_ksettings(struct net_device *netdev,
+                                 const struct ethtool_link_ksettings *cmd)
+{
+       struct igc_adapter *adapter = netdev_priv(netdev);
+       struct igc_hw *hw = &adapter->hw;
+       u32 advertising;
+
+       /* When adapter in resetting mode, autoneg/speed/duplex
+        * cannot be changed
+        */
+       if (igc_check_reset_block(hw)) {
+               dev_err(&adapter->pdev->dev,
+                       "Cannot change link characteristics when reset is active.\n");
+               return -EINVAL;
+       }
+
+       /* MDI setting is only allowed when autoneg enabled because
+        * some hardware doesn't allow MDI setting when speed or
+        * duplex is forced.
+        */
+       if (cmd->base.eth_tp_mdix_ctrl) {
+               if (cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO &&
+                   cmd->base.autoneg != AUTONEG_ENABLE) {
+                       dev_err(&adapter->pdev->dev, "forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n");
+                       return -EINVAL;
+               }
+       }
+
+       while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
+               usleep_range(1000, 2000);
+
+       ethtool_convert_link_mode_to_legacy_u32(&advertising,
+                                               cmd->link_modes.advertising);
+
+       if (cmd->base.autoneg == AUTONEG_ENABLE) {
+               hw->mac.autoneg = 1;
+               hw->phy.autoneg_advertised = advertising;
+               if (adapter->fc_autoneg)
+                       hw->fc.requested_mode = igc_fc_default;
+       } else {
+               /* calling this overrides forced MDI setting */
+               dev_info(&adapter->pdev->dev,
+                        "Force mode currently not supported\n");
+       }
+
+       /* MDI-X => 2; MDI => 1; Auto => 3 */
+       if (cmd->base.eth_tp_mdix_ctrl) {
+               /* fix up the value for auto (3 => 0) as zero is mapped
+                * internally to auto
+                */
+               if (cmd->base.eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO)
+                       hw->phy.mdix = AUTO_ALL_MODES;
+               else
+                       hw->phy.mdix = cmd->base.eth_tp_mdix_ctrl;
+       }
+
+       /* reset the link */
+       if (netif_running(adapter->netdev)) {
+               igc_down(adapter);
+               igc_up(adapter);
+       } else {
+               igc_reset(adapter);
+       }
+
+       clear_bit(__IGC_RESETTING, &adapter->state);
+
+       return 0;
+}
+
+static const struct ethtool_ops igc_ethtool_ops = {
+       .get_drvinfo            = igc_get_drvinfo,
+       .get_regs_len           = igc_get_regs_len,
+       .get_regs               = igc_get_regs,
+       .get_msglevel           = igc_get_msglevel,
+       .set_msglevel           = igc_set_msglevel,
+       .nway_reset             = igc_nway_reset,
+       .get_link               = igc_get_link,
+       .get_eeprom_len         = igc_get_eeprom_len,
+       .get_eeprom             = igc_get_eeprom,
+       .set_eeprom             = igc_set_eeprom,
+       .get_ringparam          = igc_get_ringparam,
+       .set_ringparam          = igc_set_ringparam,
+       .get_pauseparam         = igc_get_pauseparam,
+       .set_pauseparam         = igc_set_pauseparam,
+       .get_coalesce           = igc_get_coalesce,
+       .set_coalesce           = igc_set_coalesce,
+       .get_rxfh_indir_size    = igc_get_rxfh_indir_size,
+       .get_rxfh               = igc_get_rxfh,
+       .set_rxfh               = igc_set_rxfh,
+       .get_channels           = igc_get_channels,
+       .set_channels           = igc_set_channels,
+       .get_priv_flags         = igc_get_priv_flags,
+       .set_priv_flags         = igc_set_priv_flags,
+       .begin                  = igc_ethtool_begin,
+       .complete               = igc_ethtool_complete,
+       .get_link_ksettings     = igc_get_link_ksettings,
+       .set_link_ksettings     = igc_set_link_ksettings,
+};
+
+void igc_set_ethtool_ops(struct net_device *netdev)
+{
+       netdev->ethtool_ops = &igc_ethtool_ops;
+}
index c50414f..7c88b7b 100644 (file)
@@ -55,6 +55,7 @@ enum igc_media_type {
 
 enum igc_nvm_type {
        igc_nvm_unknown = 0,
+       igc_nvm_eeprom_spi,
        igc_nvm_flash_hw,
        igc_nvm_invm,
 };
index f201830..87a1187 100644 (file)
@@ -12,6 +12,8 @@
 #define DRV_VERSION    "0.0.1-k"
 #define DRV_SUMMARY    "Intel(R) 2.5G Ethernet Linux Driver"
 
+#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
+
 static int debug = -1;
 
 MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>");
@@ -66,7 +68,7 @@ enum latency_range {
        latency_invalid = 255
 };
 
-static void igc_reset(struct igc_adapter *adapter)
+void igc_reset(struct igc_adapter *adapter)
 {
        struct pci_dev *pdev = adapter->pdev;
        struct igc_hw *hw = &adapter->hw;
@@ -150,7 +152,7 @@ static void igc_get_hw_control(struct igc_adapter *adapter)
  *
  * Free all transmit software resources
  */
-static void igc_free_tx_resources(struct igc_ring *tx_ring)
+void igc_free_tx_resources(struct igc_ring *tx_ring)
 {
        igc_clean_tx_ring(tx_ring);
 
@@ -261,7 +263,7 @@ static void igc_clean_all_tx_rings(struct igc_adapter *adapter)
  *
  * Return 0 on success, negative on failure
  */
-static int igc_setup_tx_resources(struct igc_ring *tx_ring)
+int igc_setup_tx_resources(struct igc_ring *tx_ring)
 {
        struct device *dev = tx_ring->dev;
        int size = 0;
@@ -381,7 +383,7 @@ static void igc_clean_all_rx_rings(struct igc_adapter *adapter)
  *
  * Free all receive software resources
  */
-static void igc_free_rx_resources(struct igc_ring *rx_ring)
+void igc_free_rx_resources(struct igc_ring *rx_ring)
 {
        igc_clean_rx_ring(rx_ring);
 
@@ -418,7 +420,7 @@ static void igc_free_all_rx_resources(struct igc_adapter *adapter)
  *
  * Returns 0 on success, negative on failure
  */
-static int igc_setup_rx_resources(struct igc_ring *rx_ring)
+int igc_setup_rx_resources(struct igc_ring *rx_ring)
 {
        struct device *dev = rx_ring->dev;
        int size, desc_len;
@@ -1703,7 +1705,7 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget)
  * igc_up - Open the interface and prepare it to handle traffic
  * @adapter: board private structure
  */
-static void igc_up(struct igc_adapter *adapter)
+void igc_up(struct igc_adapter *adapter)
 {
        struct igc_hw *hw = &adapter->hw;
        int i = 0;
@@ -1748,7 +1750,7 @@ static void igc_nfc_filter_exit(struct igc_adapter *adapter)
  * igc_down - Close the interface
  * @adapter: board private structure
  */
-static void igc_down(struct igc_adapter *adapter)
+void igc_down(struct igc_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
        struct igc_hw *hw = &adapter->hw;
@@ -1810,7 +1812,7 @@ static void igc_down(struct igc_adapter *adapter)
        igc_clean_all_rx_rings(adapter);
 }
 
-static void igc_reinit_locked(struct igc_adapter *adapter)
+void igc_reinit_locked(struct igc_adapter *adapter)
 {
        WARN_ON(in_interrupt());
        while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
@@ -1922,7 +1924,7 @@ static void igc_configure(struct igc_adapter *adapter)
 
 /**
  * igc_rar_set_index - Sync RAL[index] and RAH[index] registers with MAC table
- * @adapter: Pointer to adapter structure
+ * @adapter: address of board private structure
  * @index: Index of the RAR entry which need to be synced with MAC table
  */
 static void igc_rar_set_index(struct igc_adapter *adapter, u32 index)
@@ -2298,7 +2300,7 @@ static void igc_update_phy_info(struct timer_list *t)
  * igc_has_link - check shared code for link and determine up/down
  * @adapter: pointer to driver private info
  */
-static bool igc_has_link(struct igc_adapter *adapter)
+bool igc_has_link(struct igc_adapter *adapter)
 {
        struct igc_hw *hw = &adapter->hw;
        bool link_active = false;
@@ -2956,22 +2958,21 @@ static int igc_alloc_q_vector(struct igc_adapter *adapter,
 {
        struct igc_q_vector *q_vector;
        struct igc_ring *ring;
-       int ring_count, size;
+       int ring_count;
 
        /* igc only supports 1 Tx and/or 1 Rx queue per vector */
        if (txr_count > 1 || rxr_count > 1)
                return -ENOMEM;
 
        ring_count = txr_count + rxr_count;
-       size = sizeof(struct igc_q_vector) +
-               (sizeof(struct igc_ring) * ring_count);
 
        /* allocate q_vector and rings */
        q_vector = adapter->q_vector[v_idx];
        if (!q_vector)
-               q_vector = kzalloc(size, GFP_KERNEL);
+               q_vector = kzalloc(struct_size(q_vector, ring, ring_count),
+                                  GFP_KERNEL);
        else
-               memset(q_vector, 0, size);
+               memset(q_vector, 0, struct_size(q_vector, ring, ring_count));
        if (!q_vector)
                return -ENOMEM;
 
@@ -3501,6 +3502,57 @@ u32 igc_rd32(struct igc_hw *hw, u32 reg)
        return value;
 }
 
+int igc_set_spd_dplx(struct igc_adapter *adapter, u32 spd, u8 dplx)
+{
+       struct pci_dev *pdev = adapter->pdev;
+       struct igc_mac_info *mac = &adapter->hw.mac;
+
+       mac->autoneg = 0;
+
+       /* Make sure dplx is at most 1 bit and lsb of speed is not set
+        * for the switch() below to work
+        */
+       if ((spd & 1) || (dplx & ~1))
+               goto err_inval;
+
+       switch (spd + dplx) {
+       case SPEED_10 + DUPLEX_HALF:
+               mac->forced_speed_duplex = ADVERTISE_10_HALF;
+               break;
+       case SPEED_10 + DUPLEX_FULL:
+               mac->forced_speed_duplex = ADVERTISE_10_FULL;
+               break;
+       case SPEED_100 + DUPLEX_HALF:
+               mac->forced_speed_duplex = ADVERTISE_100_HALF;
+               break;
+       case SPEED_100 + DUPLEX_FULL:
+               mac->forced_speed_duplex = ADVERTISE_100_FULL;
+               break;
+       case SPEED_1000 + DUPLEX_FULL:
+               mac->autoneg = 1;
+               adapter->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL;
+               break;
+       case SPEED_1000 + DUPLEX_HALF: /* not supported */
+               goto err_inval;
+       case SPEED_2500 + DUPLEX_FULL:
+               mac->autoneg = 1;
+               adapter->hw.phy.autoneg_advertised = ADVERTISE_2500_FULL;
+               break;
+       case SPEED_2500 + DUPLEX_HALF: /* not supported */
+       default:
+               goto err_inval;
+       }
+
+       /* clear MDI, MDI(-X) override is only allowed when autoneg enabled */
+       adapter->hw.phy.mdix = AUTO_ALL_MODES;
+
+       return 0;
+
+err_inval:
+       dev_err(&pdev->dev, "Unsupported Speed/Duplex configuration\n");
+       return -EINVAL;
+}
+
 /**
  * igc_probe - Device Initialization Routine
  * @pdev: PCI device information struct
@@ -3568,7 +3620,7 @@ static int igc_probe(struct pci_dev *pdev,
        hw = &adapter->hw;
        hw->back = adapter;
        adapter->port_num = hw->bus.func;
-       adapter->msg_enable = GENMASK(debug - 1, 0);
+       adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
 
        err = pci_save_state(pdev);
        if (err)
@@ -3584,7 +3636,7 @@ static int igc_probe(struct pci_dev *pdev,
        hw->hw_addr = adapter->io_addr;
 
        netdev->netdev_ops = &igc_netdev_ops;
-
+       igc_set_ethtool_ops(netdev);
        netdev->watchdog_timeo = 5 * HZ;
 
        netdev->mem_start = pci_resource_start(pdev, 0);
@@ -3744,8 +3796,8 @@ static struct pci_driver igc_driver = {
        .remove   = igc_remove,
 };
 
-static void igc_set_flag_queue_pairs(struct igc_adapter *adapter,
-                                    const u32 max_rss_queues)
+void igc_set_flag_queue_pairs(struct igc_adapter *adapter,
+                             const u32 max_rss_queues)
 {
        /* Determine if we need to pair queues. */
        /* If rss_queues > half of max_rss_queues, pair the queues in
@@ -3757,7 +3809,7 @@ static void igc_set_flag_queue_pairs(struct igc_adapter *adapter,
                adapter->flags &= ~IGC_FLAG_QUEUE_PAIRS;
 }
 
-static unsigned int igc_get_max_rss_queues(struct igc_adapter *adapter)
+unsigned int igc_get_max_rss_queues(struct igc_adapter *adapter)
 {
        unsigned int max_rss_queues;
 
@@ -3837,6 +3889,32 @@ static int igc_sw_init(struct igc_adapter *adapter)
 }
 
 /**
+ * igc_reinit_queues - return error
+ * @adapter: pointer to adapter structure
+ */
+int igc_reinit_queues(struct igc_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+       struct pci_dev *pdev = adapter->pdev;
+       int err = 0;
+
+       if (netif_running(netdev))
+               igc_close(netdev);
+
+       igc_reset_interrupt_capability(adapter);
+
+       if (igc_init_interrupt_scheme(adapter, true)) {
+               dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
+               return -ENOMEM;
+       }
+
+       if (netif_running(netdev))
+               err = igc_open(netdev);
+
+       return err;
+}
+
+/**
  * igc_get_hw_dev - return device
  * @hw: pointer to hardware structure
  *
index 38e43e6..4c8f96a 100644 (file)
@@ -152,7 +152,6 @@ void igc_power_down_phy_copper(struct igc_hw *hw)
 s32 igc_check_downshift(struct igc_hw *hw)
 {
        struct igc_phy_info *phy = &hw->phy;
-       u16 phy_data, offset, mask;
        s32 ret_val;
 
        switch (phy->type) {
@@ -161,15 +160,8 @@ s32 igc_check_downshift(struct igc_hw *hw)
                /* speed downshift not supported */
                phy->speed_downgraded = false;
                ret_val = 0;
-               goto out;
        }
 
-       ret_val = phy->ops.read_reg(hw, offset, &phy_data);
-
-       if (!ret_val)
-               phy->speed_downgraded = (phy_data & mask) ? true : false;
-
-out:
        return ret_val;
 }
 
index a1bd321..5afe7a8 100644 (file)
@@ -80,6 +80,9 @@
 /* MSI-X Table Register Descriptions */
 #define IGC_PBACL              0x05B68  /* MSIx PBA Clear - R/W 1 to clear */
 
+/* Redirection Table - RW Array */
+#define IGC_RETA(_i)           (0x05C00 + ((_i) * 4))
+
 /* Receive Register Descriptions */
 #define IGC_RCTL               0x00100  /* Rx Control - RW */
 #define IGC_SRRCTL(_n)         (0x0C00C + ((_n) * 0x40))
 #define IGC_HGOTCL     0x04130  /* Host Good Octets Transmit Count Low */
 #define IGC_HGOTCH     0x04134  /* Host Good Octets Transmit Count High */
 #define IGC_LENERRS    0x04138  /* Length Errors Count */
-#define IGC_SCVPC      0x04228  /* SerDes/SGMII Code Violation Pkt Count */
 #define IGC_HRMPC      0x0A018  /* Header Redirection Missed Packet Count */
 
 /* Management registers */
index 1e49716..109f8de 100644 (file)
@@ -1048,7 +1048,7 @@ mac_reset_top:
         * clear the multicast table.  Also reset num_rar_entries to 128,
         * since we modify this value when programming the SAN MAC address.
         */
-       hw->mac.num_rar_entries = 128;
+       hw->mac.num_rar_entries = IXGBE_82599_RAR_ENTRIES;
        hw->mac.ops.init_rx_addrs(hw);
 
        /* Store the permanent SAN mac address */
index 62e6499..cc3196a 100644 (file)
@@ -836,12 +836,10 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter,
        struct ixgbe_ring *ring;
        int node = NUMA_NO_NODE;
        int cpu = -1;
-       int ring_count, size;
+       int ring_count;
        u8 tcs = adapter->hw_tcs;
 
        ring_count = txr_count + rxr_count + xdp_count;
-       size = sizeof(struct ixgbe_q_vector) +
-              (sizeof(struct ixgbe_ring) * ring_count);
 
        /* customize cpu for Flow Director mapping */
        if ((tcs <= 1) && !(adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)) {
@@ -855,9 +853,11 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter,
        }
 
        /* allocate q_vector and rings */
-       q_vector = kzalloc_node(size, GFP_KERNEL, node);
+       q_vector = kzalloc_node(struct_size(q_vector, ring, ring_count),
+                               GFP_KERNEL, node);
        if (!q_vector)
-               q_vector = kzalloc(size, GFP_KERNEL);
+               q_vector = kzalloc(struct_size(q_vector, ring, ring_count),
+                                  GFP_KERNEL);
        if (!q_vector)
                return -ENOMEM;
 
index b53087a..38c430b 100644 (file)
@@ -10280,9 +10280,6 @@ static int ixgbe_xdp(struct net_device *dev, struct netdev_bpf *xdp)
                xdp->prog_id = adapter->xdp_prog ?
                        adapter->xdp_prog->aux->id : 0;
                return 0;
-       case XDP_QUERY_XSK_UMEM:
-               return ixgbe_xsk_umem_query(adapter, &xdp->xsk.umem,
-                                           xdp->xsk.queue_id);
        case XDP_SETUP_XSK_UMEM:
                return ixgbe_xsk_umem_setup(adapter, xdp->xsk.umem,
                                            xdp->xsk.queue_id);
index 53d4089..d93a690 100644 (file)
@@ -30,8 +30,6 @@ void ixgbe_txrx_ring_enable(struct ixgbe_adapter *adapter, int ring);
 
 struct xdp_umem *ixgbe_xsk_umem(struct ixgbe_adapter *adapter,
                                struct ixgbe_ring *ring);
-int ixgbe_xsk_umem_query(struct ixgbe_adapter *adapter, struct xdp_umem **umem,
-                        u16 qid);
 int ixgbe_xsk_umem_setup(struct ixgbe_adapter *adapter, struct xdp_umem *umem,
                         u16 qid);
 
index 65c3e2c..9887070 100644 (file)
@@ -174,23 +174,6 @@ static int ixgbe_xsk_umem_disable(struct ixgbe_adapter *adapter, u16 qid)
        return 0;
 }
 
-int ixgbe_xsk_umem_query(struct ixgbe_adapter *adapter, struct xdp_umem **umem,
-                        u16 qid)
-{
-       if (qid >= adapter->num_rx_queues)
-               return -EINVAL;
-
-       if (adapter->xsk_umems) {
-               if (qid >= adapter->num_xsk_umems)
-                       return -EINVAL;
-               *umem = adapter->xsk_umems[qid];
-               return 0;
-       }
-
-       *umem = NULL;
-       return 0;
-}
-
 int ixgbe_xsk_umem_setup(struct ixgbe_adapter *adapter, struct xdp_umem *umem,
                         u16 qid)
 {
index 32ac904..f9bb890 100644 (file)
@@ -112,10 +112,12 @@ struct ltq_etop_priv {
 static int
 ltq_etop_alloc_skb(struct ltq_etop_chan *ch)
 {
+       struct ltq_etop_priv *priv = netdev_priv(ch->netdev);
+
        ch->skb[ch->dma.desc] = netdev_alloc_skb(ch->netdev, MAX_DMA_DATA_LEN);
        if (!ch->skb[ch->dma.desc])
                return -ENOMEM;
-       ch->dma.desc_base[ch->dma.desc].addr = dma_map_single(NULL,
+       ch->dma.desc_base[ch->dma.desc].addr = dma_map_single(&priv->pdev->dev,
                ch->skb[ch->dma.desc]->data, MAX_DMA_DATA_LEN,
                DMA_FROM_DEVICE);
        ch->dma.desc_base[ch->dma.desc].addr =
@@ -487,7 +489,7 @@ ltq_etop_tx(struct sk_buff *skb, struct net_device *dev)
        netif_trans_update(dev);
 
        spin_lock_irqsave(&priv->lock, flags);
-       desc->addr = ((unsigned int) dma_map_single(NULL, skb->data, len,
+       desc->addr = ((unsigned int) dma_map_single(&priv->pdev->dev, skb->data, len,
                                                DMA_TO_DEVICE)) - byte_offset;
        wmb();
        desc->ctl = LTQ_DMA_OWN | LTQ_DMA_SOP | LTQ_DMA_EOP |
index 9d4568e..d134d35 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/of_irq.h>
 #include <linux/of_mdio.h>
 #include <linux/of_net.h>
+#include <linux/phy/phy.h>
 #include <linux/phy.h>
 #include <linux/phylink.h>
 #include <linux/platform_device.h>
@@ -436,6 +437,7 @@ struct mvneta_port {
        struct device_node *dn;
        unsigned int tx_csum_limit;
        struct phylink *phylink;
+       struct phy *comphy;
 
        struct mvneta_bm *bm_priv;
        struct mvneta_bm_pool *pool_long;
@@ -3151,6 +3153,8 @@ static void mvneta_start_dev(struct mvneta_port *pp)
 {
        int cpu;
 
+       WARN_ON(phy_power_on(pp->comphy));
+
        mvneta_max_rx_size_set(pp, pp->pkt_size);
        mvneta_txq_max_tx_size_set(pp, pp->pkt_size);
 
@@ -3213,6 +3217,8 @@ static void mvneta_stop_dev(struct mvneta_port *pp)
 
        mvneta_tx_reset(pp);
        mvneta_rx_reset(pp);
+
+       WARN_ON(phy_power_off(pp->comphy));
 }
 
 static void mvneta_percpu_enable(void *arg)
@@ -3338,6 +3344,7 @@ static int mvneta_set_mac_addr(struct net_device *dev, void *addr)
 static void mvneta_validate(struct net_device *ndev, unsigned long *supported,
                            struct phylink_link_state *state)
 {
+       struct mvneta_port *pp = netdev_priv(ndev);
        __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
 
        /* We only support QSGMII, SGMII, 802.3z and RGMII modes */
@@ -3358,8 +3365,13 @@ static void mvneta_validate(struct net_device *ndev, unsigned long *supported,
        phylink_set(mask, Pause);
 
        /* Half-duplex at speeds higher than 100Mbit is unsupported */
-       phylink_set(mask, 1000baseT_Full);
-       phylink_set(mask, 1000baseX_Full);
+       if (pp->comphy || state->interface != PHY_INTERFACE_MODE_2500BASEX) {
+               phylink_set(mask, 1000baseT_Full);
+               phylink_set(mask, 1000baseX_Full);
+       }
+       if (pp->comphy || state->interface == PHY_INTERFACE_MODE_2500BASEX) {
+               phylink_set(mask, 2500baseX_Full);
+       }
 
        if (!phy_interface_mode_is_8023z(state->interface)) {
                /* 10M and 100M are only supported in non-802.3z mode */
@@ -3373,6 +3385,11 @@ static void mvneta_validate(struct net_device *ndev, unsigned long *supported,
                   __ETHTOOL_LINK_MODE_MASK_NBITS);
        bitmap_and(state->advertising, state->advertising, mask,
                   __ETHTOOL_LINK_MODE_MASK_NBITS);
+
+       /* We can only operate at 2500BaseX or 1000BaseX.  If requested
+        * to advertise both, only report advertising at 2500BaseX.
+        */
+       phylink_helper_basex_speed(state);
 }
 
 static int mvneta_mac_link_state(struct net_device *ndev,
@@ -3384,7 +3401,9 @@ static int mvneta_mac_link_state(struct net_device *ndev,
        gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
 
        if (gmac_stat & MVNETA_GMAC_SPEED_1000)
-               state->speed = SPEED_1000;
+               state->speed =
+                       state->interface == PHY_INTERFACE_MODE_2500BASEX ?
+                       SPEED_2500 : SPEED_1000;
        else if (gmac_stat & MVNETA_GMAC_SPEED_100)
                state->speed = SPEED_100;
        else
@@ -3499,12 +3518,20 @@ static void mvneta_mac_config(struct net_device *ndev, unsigned int mode,
                            MVNETA_GMAC_FORCE_LINK_DOWN);
        }
 
+
        /* When at 2.5G, the link partner can send frames with shortened
         * preambles.
         */
        if (state->speed == SPEED_2500)
                new_ctrl4 |= MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE;
 
+       if (pp->comphy &&
+           (state->interface == PHY_INTERFACE_MODE_SGMII ||
+            state->interface == PHY_INTERFACE_MODE_1000BASEX ||
+            state->interface == PHY_INTERFACE_MODE_2500BASEX))
+               WARN_ON(phy_set_mode_ext(pp->comphy, PHY_MODE_ETHERNET,
+                                        state->interface));
+
        if (new_ctrl0 != gmac_ctrl0)
                mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0);
        if (new_ctrl2 != gmac_ctrl2)
@@ -4404,7 +4431,7 @@ static int mvneta_port_power_up(struct mvneta_port *pp, int phy_mode)
        if (phy_mode == PHY_INTERFACE_MODE_QSGMII)
                mvreg_write(pp, MVNETA_SERDES_CFG, MVNETA_QSGMII_SERDES_PROTO);
        else if (phy_mode == PHY_INTERFACE_MODE_SGMII ||
-                phy_mode == PHY_INTERFACE_MODE_1000BASEX)
+                phy_interface_mode_is_8023z(phy_mode))
                mvreg_write(pp, MVNETA_SERDES_CFG, MVNETA_SGMII_SERDES_PROTO);
        else if (!phy_interface_mode_is_rgmii(phy_mode))
                return -EINVAL;
@@ -4421,6 +4448,7 @@ static int mvneta_probe(struct platform_device *pdev)
        struct mvneta_port *pp;
        struct net_device *dev;
        struct phylink *phylink;
+       struct phy *comphy;
        const char *dt_mac_addr;
        char hw_mac_addr[ETH_ALEN];
        const char *mac_from;
@@ -4446,6 +4474,14 @@ static int mvneta_probe(struct platform_device *pdev)
                goto err_free_irq;
        }
 
+       comphy = devm_of_phy_get(&pdev->dev, dn, NULL);
+       if (comphy == ERR_PTR(-EPROBE_DEFER)) {
+               err = -EPROBE_DEFER;
+               goto err_free_irq;
+       } else if (IS_ERR(comphy)) {
+               comphy = NULL;
+       }
+
        phylink = phylink_create(dev, pdev->dev.fwnode, phy_mode,
                                 &mvneta_phylink_ops);
        if (IS_ERR(phylink)) {
@@ -4462,6 +4498,7 @@ static int mvneta_probe(struct platform_device *pdev)
        pp = netdev_priv(dev);
        spin_lock_init(&pp->lock);
        pp->phylink = phylink;
+       pp->comphy = comphy;
        pp->phy_interface = phy_mode;
        pp->dn = dn;
 
index 398328f..96e3f06 100644 (file)
 #define     MVPP2_GMAC_STATUS0_GMII_SPEED      BIT(1)
 #define     MVPP2_GMAC_STATUS0_MII_SPEED       BIT(2)
 #define     MVPP2_GMAC_STATUS0_FULL_DUPLEX     BIT(3)
-#define     MVPP2_GMAC_STATUS0_RX_PAUSE                BIT(6)
-#define     MVPP2_GMAC_STATUS0_TX_PAUSE                BIT(7)
+#define     MVPP2_GMAC_STATUS0_RX_PAUSE                BIT(4)
+#define     MVPP2_GMAC_STATUS0_TX_PAUSE                BIT(5)
 #define     MVPP2_GMAC_STATUS0_AN_COMPLETE     BIT(11)
 #define MVPP2_GMAC_PORT_FIFO_CFG_1_REG         0x1c
 #define     MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS     6
index 16066c2..191d9ce 100644 (file)
@@ -965,6 +965,11 @@ mvpp2_shared_interrupt_mask_unmask(struct mvpp2_port *port, bool mask)
 }
 
 /* Port configuration routines */
+static bool mvpp2_is_xlg(phy_interface_t interface)
+{
+       return interface == PHY_INTERFACE_MODE_10GKR ||
+              interface == PHY_INTERFACE_MODE_XAUI;
+}
 
 static void mvpp22_gop_init_rgmii(struct mvpp2_port *port)
 {
@@ -1090,9 +1095,8 @@ static void mvpp22_gop_unmask_irq(struct mvpp2_port *port)
        u32 val;
 
        if (phy_interface_mode_is_rgmii(port->phy_interface) ||
-           port->phy_interface == PHY_INTERFACE_MODE_SGMII ||
-           port->phy_interface == PHY_INTERFACE_MODE_1000BASEX ||
-           port->phy_interface == PHY_INTERFACE_MODE_2500BASEX) {
+           phy_interface_mode_is_8023z(port->phy_interface) ||
+           port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
                /* Enable the GMAC link status irq for this port */
                val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK);
                val |= MVPP22_GMAC_INT_SUM_MASK_LINK_STAT;
@@ -1102,7 +1106,7 @@ static void mvpp22_gop_unmask_irq(struct mvpp2_port *port)
        if (port->gop_id == 0) {
                /* Enable the XLG/GIG irqs for this port */
                val = readl(port->base + MVPP22_XLG_EXT_INT_MASK);
-               if (port->phy_interface == PHY_INTERFACE_MODE_10GKR)
+               if (mvpp2_is_xlg(port->phy_interface))
                        val |= MVPP22_XLG_EXT_INT_MASK_XLG;
                else
                        val |= MVPP22_XLG_EXT_INT_MASK_GIG;
@@ -1122,9 +1126,8 @@ static void mvpp22_gop_mask_irq(struct mvpp2_port *port)
        }
 
        if (phy_interface_mode_is_rgmii(port->phy_interface) ||
-           port->phy_interface == PHY_INTERFACE_MODE_SGMII ||
-           port->phy_interface == PHY_INTERFACE_MODE_1000BASEX ||
-           port->phy_interface == PHY_INTERFACE_MODE_2500BASEX) {
+           phy_interface_mode_is_8023z(port->phy_interface) ||
+           port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
                val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK);
                val &= ~MVPP22_GMAC_INT_SUM_MASK_LINK_STAT;
                writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK);
@@ -1135,10 +1138,10 @@ static void mvpp22_gop_setup_irq(struct mvpp2_port *port)
 {
        u32 val;
 
-       if (phy_interface_mode_is_rgmii(port->phy_interface) ||
-           port->phy_interface == PHY_INTERFACE_MODE_SGMII ||
-           port->phy_interface == PHY_INTERFACE_MODE_1000BASEX ||
-           port->phy_interface == PHY_INTERFACE_MODE_2500BASEX) {
+       if (port->phylink ||
+           phy_interface_mode_is_rgmii(port->phy_interface) ||
+           phy_interface_mode_is_8023z(port->phy_interface) ||
+           port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
                val = readl(port->base + MVPP22_GMAC_INT_MASK);
                val |= MVPP22_GMAC_INT_MASK_LINK_STAT;
                writel(val, port->base + MVPP22_GMAC_INT_MASK);
@@ -1183,9 +1186,7 @@ static void mvpp2_port_enable(struct mvpp2_port *port)
        u32 val;
 
        /* Only GOP port 0 has an XLG MAC */
-       if (port->gop_id == 0 &&
-           (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
-            port->phy_interface == PHY_INTERFACE_MODE_10GKR)) {
+       if (port->gop_id == 0 && mvpp2_is_xlg(port->phy_interface)) {
                val = readl(port->base + MVPP22_XLG_CTRL0_REG);
                val |= MVPP22_XLG_CTRL0_PORT_EN |
                       MVPP22_XLG_CTRL0_MAC_RESET_DIS;
@@ -1204,9 +1205,7 @@ static void mvpp2_port_disable(struct mvpp2_port *port)
        u32 val;
 
        /* Only GOP port 0 has an XLG MAC */
-       if (port->gop_id == 0 &&
-           (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
-            port->phy_interface == PHY_INTERFACE_MODE_10GKR)) {
+       if (port->gop_id == 0 && mvpp2_is_xlg(port->phy_interface)) {
                val = readl(port->base + MVPP22_XLG_CTRL0_REG);
                val &= ~MVPP22_XLG_CTRL0_PORT_EN;
                writel(val, port->base + MVPP22_XLG_CTRL0_REG);
@@ -1244,9 +1243,8 @@ static void mvpp2_port_loopback_set(struct mvpp2_port *port,
        else
                val &= ~MVPP2_GMAC_GMII_LB_EN_MASK;
 
-       if (port->phy_interface == PHY_INTERFACE_MODE_SGMII ||
-           port->phy_interface == PHY_INTERFACE_MODE_1000BASEX ||
-           port->phy_interface == PHY_INTERFACE_MODE_2500BASEX)
+       if (phy_interface_mode_is_8023z(port->phy_interface) ||
+           port->phy_interface == PHY_INTERFACE_MODE_SGMII)
                val |= MVPP2_GMAC_PCS_LB_EN_MASK;
        else
                val &= ~MVPP2_GMAC_PCS_LB_EN_MASK;
@@ -1380,13 +1378,9 @@ static void mvpp2_port_reset(struct mvpp2_port *port)
        for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_regs); i++)
                mvpp2_read_count(port, &mvpp2_ethtool_regs[i]);
 
-       val = readl(port->base + MVPP2_GMAC_CTRL_2_REG) &
-                   ~MVPP2_GMAC_PORT_RESET_MASK;
+       val = readl(port->base + MVPP2_GMAC_CTRL_2_REG) |
+             MVPP2_GMAC_PORT_RESET_MASK;
        writel(val, port->base + MVPP2_GMAC_CTRL_2_REG);
-
-       while (readl(port->base + MVPP2_GMAC_CTRL_2_REG) &
-              MVPP2_GMAC_PORT_RESET_MASK)
-               continue;
 }
 
 /* Change maximum receive size of the port */
@@ -2462,8 +2456,7 @@ static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id)
 
        mvpp22_gop_mask_irq(port);
 
-       if (port->gop_id == 0 &&
-           port->phy_interface == PHY_INTERFACE_MODE_10GKR) {
+       if (port->gop_id == 0 && mvpp2_is_xlg(port->phy_interface)) {
                val = readl(port->base + MVPP22_XLG_INT_STAT);
                if (val & MVPP22_XLG_INT_STAT_LINK) {
                        event = true;
@@ -2472,9 +2465,8 @@ static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id)
                                link = true;
                }
        } else if (phy_interface_mode_is_rgmii(port->phy_interface) ||
-                  port->phy_interface == PHY_INTERFACE_MODE_SGMII ||
-                  port->phy_interface == PHY_INTERFACE_MODE_1000BASEX ||
-                  port->phy_interface == PHY_INTERFACE_MODE_2500BASEX) {
+                  phy_interface_mode_is_8023z(port->phy_interface) ||
+                  port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
                val = readl(port->base + MVPP22_GMAC_INT_STAT);
                if (val & MVPP22_GMAC_INT_STAT_LINK) {
                        event = true;
@@ -3154,8 +3146,7 @@ static void mvpp22_mode_reconfigure(struct mvpp2_port *port)
                ctrl3 = readl(port->base + MVPP22_XLG_CTRL3_REG);
                ctrl3 &= ~MVPP22_XLG_CTRL3_MACMODESELECT_MASK;
 
-               if (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
-                   port->phy_interface == PHY_INTERFACE_MODE_10GKR)
+               if (mvpp2_is_xlg(port->phy_interface))
                        ctrl3 |= MVPP22_XLG_CTRL3_MACMODESELECT_10G;
                else
                        ctrl3 |= MVPP22_XLG_CTRL3_MACMODESELECT_GMAC;
@@ -3163,9 +3154,7 @@ static void mvpp22_mode_reconfigure(struct mvpp2_port *port)
                writel(ctrl3, port->base + MVPP22_XLG_CTRL3_REG);
        }
 
-       if (port->gop_id == 0 &&
-           (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
-            port->phy_interface == PHY_INTERFACE_MODE_10GKR))
+       if (port->gop_id == 0 && mvpp2_is_xlg(port->phy_interface))
                mvpp2_xlg_max_rx_size_set(port);
        else
                mvpp2_gmac_max_rx_size_set(port);
@@ -4505,17 +4494,12 @@ static int mvpp2_phylink_mac_link_state(struct net_device *dev,
 static void mvpp2_mac_an_restart(struct net_device *dev)
 {
        struct mvpp2_port *port = netdev_priv(dev);
-       u32 val;
+       u32 val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
 
-       if (port->phy_interface != PHY_INTERFACE_MODE_SGMII)
-               return;
-
-       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       /* The RESTART_AN bit is cleared by the h/w after restarting the AN
-        * process.
-        */
-       val |= MVPP2_GMAC_IN_BAND_RESTART_AN | MVPP2_GMAC_IN_BAND_AUTONEG;
-       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       writel(val | MVPP2_GMAC_IN_BAND_RESTART_AN,
+              port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       writel(val & ~MVPP2_GMAC_IN_BAND_RESTART_AN,
+              port->base + MVPP2_GMAC_AUTONEG_CONFIG);
 }
 
 static void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode,
@@ -4528,8 +4512,13 @@ static void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode,
 
        if (state->pause & MLO_PAUSE_TX)
                ctrl0 |= MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN;
+       else
+               ctrl0 &= ~MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN;
+
        if (state->pause & MLO_PAUSE_RX)
                ctrl0 |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN;
+       else
+               ctrl0 &= ~MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN;
 
        ctrl4 &= ~MVPP22_XLG_CTRL4_MACMODSELECT_GMAC;
        ctrl4 |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC |
@@ -4542,122 +4531,172 @@ static void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode,
 static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode,
                              const struct phylink_link_state *state)
 {
-       u32 an, ctrl0, ctrl2, ctrl4;
-
-       an = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       ctrl0 = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
-       ctrl2 = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
-       ctrl4 = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
-
-       /* Force link down */
-       an &= ~MVPP2_GMAC_FORCE_LINK_PASS;
-       an |= MVPP2_GMAC_FORCE_LINK_DOWN;
-       writel(an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       u32 old_an, an;
+       u32 old_ctrl0, ctrl0;
+       u32 old_ctrl2, ctrl2;
+       u32 old_ctrl4, ctrl4;
 
-       /* Set the GMAC in a reset state */
-       ctrl2 |= MVPP2_GMAC_PORT_RESET_MASK;
-       writel(ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG);
+       old_an = an = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       old_ctrl0 = ctrl0 = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
+       old_ctrl2 = ctrl2 = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
+       old_ctrl4 = ctrl4 = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
 
        an &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | MVPP2_GMAC_CONFIG_GMII_SPEED |
                MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FC_ADV_EN |
                MVPP2_GMAC_FC_ADV_ASM_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG |
                MVPP2_GMAC_CONFIG_FULL_DUPLEX | MVPP2_GMAC_AN_DUPLEX_EN |
-               MVPP2_GMAC_FORCE_LINK_DOWN);
+               MVPP2_GMAC_IN_BAND_AUTONEG | MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS);
        ctrl0 &= ~MVPP2_GMAC_PORT_TYPE_MASK;
-       ctrl2 &= ~(MVPP2_GMAC_PORT_RESET_MASK | MVPP2_GMAC_PCS_ENABLE_MASK);
-
-       if (state->interface == PHY_INTERFACE_MODE_1000BASEX ||
-           state->interface == PHY_INTERFACE_MODE_2500BASEX) {
-               /* 1000BaseX and 2500BaseX ports cannot negotiate speed nor can
-                * they negotiate duplex: they are always operating with a fixed
-                * speed of 1000/2500Mbps in full duplex, so force 1000/2500
-                * speed and full duplex here.
-                */
-               ctrl0 |= MVPP2_GMAC_PORT_TYPE_MASK;
-               an |= MVPP2_GMAC_CONFIG_GMII_SPEED |
-                     MVPP2_GMAC_CONFIG_FULL_DUPLEX;
-       } else if (!phy_interface_mode_is_rgmii(state->interface)) {
-               an |= MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG;
+       ctrl2 &= ~(MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PORT_RESET_MASK |
+                  MVPP2_GMAC_PCS_ENABLE_MASK);
+       ctrl4 &= ~(MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN);
+
+       /* Configure port type */
+       if (phy_interface_mode_is_8023z(state->interface)) {
+               ctrl2 |= MVPP2_GMAC_PCS_ENABLE_MASK;
+               ctrl4 &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL;
+               ctrl4 |= MVPP22_CTRL4_SYNC_BYPASS_DIS |
+                        MVPP22_CTRL4_DP_CLK_SEL |
+                        MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
+       } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+               ctrl2 |= MVPP2_GMAC_PCS_ENABLE_MASK | MVPP2_GMAC_INBAND_AN_MASK;
+               ctrl4 &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL;
+               ctrl4 |= MVPP22_CTRL4_SYNC_BYPASS_DIS |
+                        MVPP22_CTRL4_DP_CLK_SEL |
+                        MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
+       } else if (phy_interface_mode_is_rgmii(state->interface)) {
+               ctrl4 &= ~MVPP22_CTRL4_DP_CLK_SEL;
+               ctrl4 |= MVPP22_CTRL4_EXT_PIN_GMII_SEL |
+                        MVPP22_CTRL4_SYNC_BYPASS_DIS |
+                        MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
        }
 
-       if (state->duplex)
-               an |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
+       /* Configure advertisement bits */
        if (phylink_test(state->advertising, Pause))
                an |= MVPP2_GMAC_FC_ADV_EN;
        if (phylink_test(state->advertising, Asym_Pause))
                an |= MVPP2_GMAC_FC_ADV_ASM_EN;
 
-       if (state->interface == PHY_INTERFACE_MODE_SGMII ||
-           state->interface == PHY_INTERFACE_MODE_1000BASEX ||
-           state->interface == PHY_INTERFACE_MODE_2500BASEX) {
-               an |= MVPP2_GMAC_IN_BAND_AUTONEG;
-               ctrl2 |= MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK;
+       /* Configure negotiation style */
+       if (!phylink_autoneg_inband(mode)) {
+               /* Phy or fixed speed - no in-band AN */
+               if (state->duplex)
+                       an |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
 
-               ctrl4 &= ~(MVPP22_CTRL4_EXT_PIN_GMII_SEL |
-                          MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN);
-               ctrl4 |= MVPP22_CTRL4_SYNC_BYPASS_DIS |
-                        MVPP22_CTRL4_DP_CLK_SEL |
-                        MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
+               if (state->speed == SPEED_1000 || state->speed == SPEED_2500)
+                       an |= MVPP2_GMAC_CONFIG_GMII_SPEED;
+               else if (state->speed == SPEED_100)
+                       an |= MVPP2_GMAC_CONFIG_MII_SPEED;
 
                if (state->pause & MLO_PAUSE_TX)
                        ctrl4 |= MVPP22_CTRL4_TX_FC_EN;
                if (state->pause & MLO_PAUSE_RX)
                        ctrl4 |= MVPP22_CTRL4_RX_FC_EN;
-       } else if (phy_interface_mode_is_rgmii(state->interface)) {
-               an |= MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS;
+       } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+               /* SGMII in-band mode receives the speed and duplex from
+                * the PHY. Flow control information is not received. */
+               an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | MVPP2_GMAC_FORCE_LINK_PASS);
+               an |= MVPP2_GMAC_IN_BAND_AUTONEG |
+                     MVPP2_GMAC_AN_SPEED_EN |
+                     MVPP2_GMAC_AN_DUPLEX_EN;
 
-               if (state->speed == SPEED_1000)
-                       an |= MVPP2_GMAC_CONFIG_GMII_SPEED;
-               else if (state->speed == SPEED_100)
-                       an |= MVPP2_GMAC_CONFIG_MII_SPEED;
+               if (state->pause & MLO_PAUSE_TX)
+                       ctrl4 |= MVPP22_CTRL4_TX_FC_EN;
+               if (state->pause & MLO_PAUSE_RX)
+                       ctrl4 |= MVPP22_CTRL4_RX_FC_EN;
+       } else if (phy_interface_mode_is_8023z(state->interface)) {
+               /* 1000BaseX and 2500BaseX ports cannot negotiate speed nor can
+                * they negotiate duplex: they are always operating with a fixed
+                * speed of 1000/2500Mbps in full duplex, so force 1000/2500
+                * speed and full duplex here.
+                */
+               ctrl0 |= MVPP2_GMAC_PORT_TYPE_MASK;
+               an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | MVPP2_GMAC_FORCE_LINK_PASS);
+               an |= MVPP2_GMAC_IN_BAND_AUTONEG |
+                     MVPP2_GMAC_CONFIG_GMII_SPEED |
+                     MVPP2_GMAC_CONFIG_FULL_DUPLEX;
 
-               ctrl4 &= ~MVPP22_CTRL4_DP_CLK_SEL;
-               ctrl4 |= MVPP22_CTRL4_EXT_PIN_GMII_SEL |
-                        MVPP22_CTRL4_SYNC_BYPASS_DIS |
-                        MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
+               if (state->pause & MLO_PAUSE_AN && state->an_enabled) {
+                       an |= MVPP2_GMAC_FLOW_CTRL_AUTONEG;
+               } else {
+                       if (state->pause & MLO_PAUSE_TX)
+                               ctrl4 |= MVPP22_CTRL4_TX_FC_EN;
+                       if (state->pause & MLO_PAUSE_RX)
+                               ctrl4 |= MVPP22_CTRL4_RX_FC_EN;
+               }
        }
 
-       writel(ctrl0, port->base + MVPP2_GMAC_CTRL_0_REG);
-       writel(ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG);
-       writel(ctrl4, port->base + MVPP22_GMAC_CTRL_4_REG);
-       writel(an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       if ((old_ctrl0 ^ ctrl0) & MVPP2_GMAC_PORT_TYPE_MASK ||
+           (old_ctrl2 ^ ctrl2) & MVPP2_GMAC_INBAND_AN_MASK ||
+           (old_an ^ an) & MVPP2_GMAC_IN_BAND_AUTONEG) {
+               /* Force link down */
+               old_an &= ~MVPP2_GMAC_FORCE_LINK_PASS;
+               old_an |= MVPP2_GMAC_FORCE_LINK_DOWN;
+               writel(old_an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+
+               /* Set the GMAC in a reset state - do this in a way that
+                * ensures we clear it below.
+                */
+               old_ctrl2 |= MVPP2_GMAC_PORT_RESET_MASK;
+               writel(old_ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG);
+       }
+
+       if (old_ctrl0 != ctrl0)
+               writel(ctrl0, port->base + MVPP2_GMAC_CTRL_0_REG);
+       if (old_ctrl2 != ctrl2)
+               writel(ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG);
+       if (old_ctrl4 != ctrl4)
+               writel(ctrl4, port->base + MVPP22_GMAC_CTRL_4_REG);
+       if (old_an != an)
+               writel(an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+
+       if (old_ctrl2 & MVPP2_GMAC_PORT_RESET_MASK) {
+               while (readl(port->base + MVPP2_GMAC_CTRL_2_REG) &
+                      MVPP2_GMAC_PORT_RESET_MASK)
+                       continue;
+       }
 }
 
 static void mvpp2_mac_config(struct net_device *dev, unsigned int mode,
                             const struct phylink_link_state *state)
 {
        struct mvpp2_port *port = netdev_priv(dev);
+       bool change_interface = port->phy_interface != state->interface;
 
        /* Check for invalid configuration */
-       if (state->interface == PHY_INTERFACE_MODE_10GKR && port->gop_id != 0) {
+       if (mvpp2_is_xlg(state->interface) && port->gop_id != 0) {
                netdev_err(dev, "Invalid mode on %s\n", dev->name);
                return;
        }
 
        /* Make sure the port is disabled when reconfiguring the mode */
        mvpp2_port_disable(port);
+       if (change_interface) {
+               mvpp22_gop_mask_irq(port);
 
-       if (port->priv->hw_version == MVPP22 &&
-           port->phy_interface != state->interface) {
-               port->phy_interface = state->interface;
+               if (port->priv->hw_version == MVPP22) {
+                       port->phy_interface = state->interface;
 
-               /* Reconfigure the serdes lanes */
-               phy_power_off(port->comphy);
-               mvpp22_mode_reconfigure(port);
+                       /* Reconfigure the serdes lanes */
+                       phy_power_off(port->comphy);
+                       mvpp22_mode_reconfigure(port);
+               }
        }
 
        /* mac (re)configuration */
-       if (state->interface == PHY_INTERFACE_MODE_10GKR)
+       if (mvpp2_is_xlg(state->interface))
                mvpp2_xlg_config(port, mode, state);
        else if (phy_interface_mode_is_rgmii(state->interface) ||
-                state->interface == PHY_INTERFACE_MODE_SGMII ||
-                state->interface == PHY_INTERFACE_MODE_1000BASEX ||
-                state->interface == PHY_INTERFACE_MODE_2500BASEX)
+                phy_interface_mode_is_8023z(state->interface) ||
+                state->interface == PHY_INTERFACE_MODE_SGMII)
                mvpp2_gmac_config(port, mode, state);
 
        if (port->priv->hw_version == MVPP21 && port->flags & MVPP2_F_LOOPBACK)
                mvpp2_port_loopback_set(port, state);
 
+       if (change_interface)
+               mvpp22_gop_unmask_irq(port);
+
        mvpp2_port_enable(port);
 }
 
@@ -4667,12 +4706,10 @@ static void mvpp2_mac_link_up(struct net_device *dev, unsigned int mode,
        struct mvpp2_port *port = netdev_priv(dev);
        u32 val;
 
-       if (!phylink_autoneg_inband(mode) &&
-           interface != PHY_INTERFACE_MODE_10GKR) {
+       if (!phylink_autoneg_inband(mode) && !mvpp2_is_xlg(interface)) {
                val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
                val &= ~MVPP2_GMAC_FORCE_LINK_DOWN;
-               if (phy_interface_mode_is_rgmii(interface))
-                       val |= MVPP2_GMAC_FORCE_LINK_PASS;
+               val |= MVPP2_GMAC_FORCE_LINK_PASS;
                writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
        }
 
@@ -4689,8 +4726,7 @@ static void mvpp2_mac_link_down(struct net_device *dev, unsigned int mode,
        struct mvpp2_port *port = netdev_priv(dev);
        u32 val;
 
-       if (!phylink_autoneg_inband(mode) &&
-           interface != PHY_INTERFACE_MODE_10GKR) {
+       if (!phylink_autoneg_inband(mode) && !mvpp2_is_xlg(interface)) {
                val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
                val &= ~MVPP2_GMAC_FORCE_LINK_PASS;
                val |= MVPP2_GMAC_FORCE_LINK_DOWN;
index f8a6d6e..35f2142 100644 (file)
@@ -201,6 +201,7 @@ struct tx_desc {
 };
 
 struct pxa168_eth_private {
+       struct platform_device *pdev;
        int port_num;           /* User Ethernet port number    */
        int phy_addr;
        int phy_speed;
@@ -331,7 +332,7 @@ static void rxq_refill(struct net_device *dev)
                used_rx_desc = pep->rx_used_desc_q;
                p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
                size = skb_end_pointer(skb) - skb->data;
-               p_used_rx_desc->buf_ptr = dma_map_single(NULL,
+               p_used_rx_desc->buf_ptr = dma_map_single(&pep->pdev->dev,
                                                         skb->data,
                                                         size,
                                                         DMA_FROM_DEVICE);
@@ -743,7 +744,7 @@ static int txq_reclaim(struct net_device *dev, int force)
                                netdev_err(dev, "Error in TX\n");
                        dev->stats.tx_errors++;
                }
-               dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
+               dma_unmap_single(&pep->pdev->dev, addr, count, DMA_TO_DEVICE);
                if (skb)
                        dev_kfree_skb_irq(skb);
                released++;
@@ -805,7 +806,7 @@ static int rxq_process(struct net_device *dev, int budget)
                if (rx_next_curr_desc == rx_used_desc)
                        pep->rx_resource_err = 1;
                pep->rx_desc_count--;
-               dma_unmap_single(NULL, rx_desc->buf_ptr,
+               dma_unmap_single(&pep->pdev->dev, rx_desc->buf_ptr,
                                 rx_desc->buf_size,
                                 DMA_FROM_DEVICE);
                received_packets++;
@@ -1274,7 +1275,8 @@ pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
        length = skb->len;
        pep->tx_skb[tx_index] = skb;
        desc->byte_cnt = length;
-       desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
+       desc->buf_ptr = dma_map_single(&pep->pdev->dev, skb->data, length,
+                                       DMA_TO_DEVICE);
 
        skb_tx_timestamp(skb);
 
@@ -1528,6 +1530,7 @@ static int pxa168_eth_probe(struct platform_device *pdev)
        if (err)
                goto err_free_mdio;
 
+       pep->pdev = pdev;
        SET_NETDEV_DEV(dev, &pdev->dev);
        pxa168_init_hw(pep);
        err = register_netdev(dev);
index 04fd1f1..654ac53 100644 (file)
@@ -152,8 +152,10 @@ static void skge_get_regs(struct net_device *dev, struct ethtool_regs *regs,
        memset(p, 0, regs->len);
        memcpy_fromio(p, io, B3_RAM_ADDR);
 
-       memcpy_fromio(p + B3_RI_WTO_R1, io + B3_RI_WTO_R1,
-                     regs->len - B3_RI_WTO_R1);
+       if (regs->len > B3_RI_WTO_R1) {
+               memcpy_fromio(p + B3_RI_WTO_R1, io + B3_RI_WTO_R1,
+                             regs->len - B3_RI_WTO_R1);
+       }
 }
 
 /* Wake on Lan only supported on Yukon chips with rev 1 or above */
index f9149d2..43656f9 100644 (file)
@@ -1,6 +1,6 @@
 config NET_VENDOR_MEDIATEK
        bool "MediaTek ethernet driver"
-       depends on ARCH_MEDIATEK
+       depends on ARCH_MEDIATEK || SOC_MT7621
        ---help---
          If you have a Mediatek SoC with ethernet, say Y.
 
index 49f926b..94d4663 100644 (file)
@@ -1745,6 +1745,22 @@ static irqreturn_t mtk_handle_irq_tx(int irq, void *_eth)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t mtk_handle_irq(int irq, void *_eth)
+{
+       struct mtk_eth *eth = _eth;
+
+       if (mtk_r32(eth, MTK_PDMA_INT_MASK) & MTK_RX_DONE_INT) {
+               if (mtk_r32(eth, MTK_PDMA_INT_STATUS) & MTK_RX_DONE_INT)
+                       mtk_handle_irq_rx(irq, _eth);
+       }
+       if (mtk_r32(eth, MTK_QDMA_INT_MASK) & MTK_TX_DONE_INT) {
+               if (mtk_r32(eth, MTK_QMTK_INT_STATUS) & MTK_TX_DONE_INT)
+                       mtk_handle_irq_tx(irq, _eth);
+       }
+
+       return IRQ_HANDLED;
+}
+
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void mtk_poll_controller(struct net_device *dev)
 {
@@ -2485,7 +2501,10 @@ static int mtk_probe(struct platform_device *pdev)
        }
 
        for (i = 0; i < 3; i++) {
-               eth->irq[i] = platform_get_irq(pdev, i);
+               if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT) && i > 0)
+                       eth->irq[i] = eth->irq[0];
+               else
+                       eth->irq[i] = platform_get_irq(pdev, i);
                if (eth->irq[i] < 0) {
                        dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
                        return -ENXIO;
@@ -2528,13 +2547,21 @@ static int mtk_probe(struct platform_device *pdev)
                        goto err_deinit_hw;
        }
 
-       err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0,
-                              dev_name(eth->dev), eth);
-       if (err)
-               goto err_free_dev;
+       if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) {
+               err = devm_request_irq(eth->dev, eth->irq[0],
+                                      mtk_handle_irq, 0,
+                                      dev_name(eth->dev), eth);
+       } else {
+               err = devm_request_irq(eth->dev, eth->irq[1],
+                                      mtk_handle_irq_tx, 0,
+                                      dev_name(eth->dev), eth);
+               if (err)
+                       goto err_free_dev;
 
-       err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0,
-                              dev_name(eth->dev), eth);
+               err = devm_request_irq(eth->dev, eth->irq[2],
+                                      mtk_handle_irq_rx, 0,
+                                      dev_name(eth->dev), eth);
+       }
        if (err)
                goto err_free_dev;
 
@@ -2607,6 +2634,12 @@ static const struct mtk_soc_data mt2701_data = {
        .required_pctl = true,
 };
 
+static const struct mtk_soc_data mt7621_data = {
+       .caps = MTK_SHARED_INT,
+       .required_clks = MT7621_CLKS_BITMAP,
+       .required_pctl = false,
+};
+
 static const struct mtk_soc_data mt7622_data = {
        .caps = MTK_DUAL_GMAC_SHARED_SGMII | MTK_GMAC1_ESW | MTK_HWLRO,
        .required_clks = MT7622_CLKS_BITMAP,
@@ -2621,6 +2654,7 @@ static const struct mtk_soc_data mt7623_data = {
 
 const struct of_device_id of_mtk_match[] = {
        { .compatible = "mediatek,mt2701-eth", .data = &mt2701_data},
+       { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data},
        { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data},
        { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data},
        {},
index 4681929..f750199 100644 (file)
 #define ETHSYS_CHIPID4_7       0x4
 #define MT7623_ETH             7623
 #define MT7622_ETH             7622
+#define MT7621_ETH             7621
 
 /* ethernet subsystem config register */
 #define ETHSYS_SYSCFG0         0x14
@@ -488,6 +489,8 @@ enum mtk_clks_map {
                                 BIT(MTK_CLK_SGMII_CDR_FB) | \
                                 BIT(MTK_CLK_SGMII_CK) | \
                                 BIT(MTK_CLK_ETH2PLL))
+#define MT7621_CLKS_BITMAP     (0)
+
 enum mtk_dev_state {
        MTK_HW_INIT,
        MTK_RESETTING
@@ -567,6 +570,7 @@ struct mtk_rx_ring {
 #define MTK_DUAL_GMAC_SHARED_SGMII     (BIT(11) | MTK_GMAC1_SGMII | \
                                         MTK_GMAC2_SGMII)
 #define MTK_HWLRO                      BIT(12)
+#define MTK_SHARED_INT                 BIT(13)
 #define MTK_HAS_CAPS(caps, _x)         (((caps) & (_x)) == (_x))
 
 /* struct mtk_eth_data -       This is the structure holding all differences
index 9a0881c..6c01314 100644 (file)
@@ -617,6 +617,8 @@ static int get_fixed_ipv6_csum(__wsum hw_checksum, struct sk_buff *skb,
 }
 #endif
 
+#define short_frame(size) ((size) <= ETH_ZLEN + ETH_FCS_LEN)
+
 /* We reach this function only after checking that any of
  * the (IPv4 | IPv6) bits are set in cqe->status.
  */
@@ -624,9 +626,20 @@ static int check_csum(struct mlx4_cqe *cqe, struct sk_buff *skb, void *va,
                      netdev_features_t dev_features)
 {
        __wsum hw_checksum = 0;
+       void *hdr;
+
+       /* CQE csum doesn't cover padding octets in short ethernet
+        * frames. And the pad field is appended prior to calculating
+        * and appending the FCS field.
+        *
+        * Detecting these padded frames requires to verify and parse
+        * IP headers, so we simply force all those small frames to skip
+        * checksum complete.
+        */
+       if (short_frame(skb->len))
+               return -EINVAL;
 
-       void *hdr = (u8 *)va + sizeof(struct ethhdr);
-
+       hdr = (u8 *)va + sizeof(struct ethhdr);
        hw_checksum = csum_unfold((__force __sum16)cqe->checksum);
 
        if (cqe->vlan_my_qpn & cpu_to_be32(MLX4_CQE_CVLAN_PRESENT_MASK) &&
@@ -819,6 +832,11 @@ xdp_drop_no_cnt:
                skb_record_rx_queue(skb, cq_ring);
 
                if (likely(dev->features & NETIF_F_RXCSUM)) {
+                       /* TODO: For IP non TCP/UDP packets when csum complete is
+                        * not an option (not supported or any other reason) we can
+                        * actually check cqe IPOK status bit and report
+                        * CHECKSUM_UNNECESSARY rather than CHECKSUM_NONE
+                        */
                        if ((cqe->status & cpu_to_be16(MLX4_CQE_STATUS_TCP |
                                                       MLX4_CQE_STATUS_UDP)) &&
                            (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPOK)) &&
index bdb8dd1..1f6e16d 100644 (file)
@@ -3981,6 +3981,7 @@ static int mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
        if (ret)
                goto err_params_unregister;
 
+       devlink_params_publish(devlink);
        pci_save_state(pdev);
        return 0;
 
index 9de9aba..82d636b 100644 (file)
@@ -13,7 +13,7 @@ obj-$(CONFIG_MLX5_CORE) += mlx5_core.o
 #
 mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \
                health.o mcg.o cq.o alloc.o qp.o port.o mr.o pd.o \
-               mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o \
+               transobj.o vport.o sriov.o fs_cmd.o fs_core.o \
                fs_counters.o rl.o lag.o dev.o events.o wq.o lib/gid.o \
                lib/devcom.o diag/fs_tracepoint.o diag/fw_tracer.o
 
@@ -22,7 +22,7 @@ mlx5_core-y :=        main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \
 #
 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_selftest.o en/port.o en/monitor_stats.o en/reporter_tx.o
 
 #
 # Netdev extra
@@ -35,7 +35,7 @@ mlx5_core-$(CONFIG_MLX5_ESWITCH)     += en_rep.o en_tc.o en/tc_tun.o
 #
 # Core extra
 #
-mlx5_core-$(CONFIG_MLX5_ESWITCH)   += eswitch.o eswitch_offloads.o
+mlx5_core-$(CONFIG_MLX5_ESWITCH)   += eswitch.o eswitch_offloads.o ecpf.o
 mlx5_core-$(CONFIG_MLX5_MPFS)      += lib/mpfs.o
 mlx5_core-$(CONFIG_VXLAN)          += lib/vxlan.o
 mlx5_core-$(CONFIG_PTP_1588_CLOCK) += lib/clock.o
index 3e0fa8a..be48c64 100644 (file)
@@ -316,6 +316,7 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
        case MLX5_CMD_OP_DESTROY_GENERAL_OBJECT:
        case MLX5_CMD_OP_DEALLOC_MEMIC:
        case MLX5_CMD_OP_PAGE_FAULT_RESUME:
+       case MLX5_CMD_OP_QUERY_HOST_PARAMS:
                return MLX5_CMD_STAT_OK;
 
        case MLX5_CMD_OP_QUERY_HCA_CAP:
@@ -627,6 +628,7 @@ const char *mlx5_command_str(int command)
        MLX5_COMMAND_STR_CASE(QUERY_MODIFY_HEADER_CONTEXT);
        MLX5_COMMAND_STR_CASE(ALLOC_MEMIC);
        MLX5_COMMAND_STR_CASE(DEALLOC_MEMIC);
+       MLX5_COMMAND_STR_CASE(QUERY_HOST_PARAMS);
        default: return "unknown command opcode";
        }
 }
@@ -1583,6 +1585,24 @@ no_trig:
        spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags);
 }
 
+void mlx5_cmd_flush(struct mlx5_core_dev *dev)
+{
+       struct mlx5_cmd *cmd = &dev->cmd;
+       int i;
+
+       for (i = 0; i < cmd->max_reg_cmds; i++)
+               while (down_trylock(&cmd->sem))
+                       mlx5_cmd_trigger_completions(dev);
+
+       while (down_trylock(&cmd->pages_sem))
+               mlx5_cmd_trigger_completions(dev);
+
+       /* Unlock cmdif */
+       up(&cmd->pages_sem);
+       for (i = 0; i < cmd->max_reg_cmds; i++)
+               up(&cmd->sem);
+}
+
 static int status_to_err(u8 status)
 {
        return status ? -1 : 0; /* TBD more meaningful codes */
@@ -1711,12 +1731,57 @@ int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
 }
 EXPORT_SYMBOL(mlx5_cmd_exec);
 
-int mlx5_cmd_exec_cb(struct mlx5_core_dev *dev, void *in, int in_size,
-                    void *out, int out_size, mlx5_cmd_cbk_t callback,
-                    void *context)
+void mlx5_cmd_init_async_ctx(struct mlx5_core_dev *dev,
+                            struct mlx5_async_ctx *ctx)
+{
+       ctx->dev = dev;
+       /* Starts at 1 to avoid doing wake_up if we are not cleaning up */
+       atomic_set(&ctx->num_inflight, 1);
+       init_waitqueue_head(&ctx->wait);
+}
+EXPORT_SYMBOL(mlx5_cmd_init_async_ctx);
+
+/**
+ * mlx5_cmd_cleanup_async_ctx - Clean up an async_ctx
+ * @ctx: The ctx to clean
+ *
+ * Upon return all callbacks given to mlx5_cmd_exec_cb() have been called. The
+ * caller must ensure that mlx5_cmd_exec_cb() is not called during or after
+ * the call mlx5_cleanup_async_ctx().
+ */
+void mlx5_cmd_cleanup_async_ctx(struct mlx5_async_ctx *ctx)
+{
+       atomic_dec(&ctx->num_inflight);
+       wait_event(ctx->wait, atomic_read(&ctx->num_inflight) == 0);
+}
+EXPORT_SYMBOL(mlx5_cmd_cleanup_async_ctx);
+
+static void mlx5_cmd_exec_cb_handler(int status, void *_work)
+{
+       struct mlx5_async_work *work = _work;
+       struct mlx5_async_ctx *ctx = work->ctx;
+
+       work->user_callback(status, work);
+       if (atomic_dec_and_test(&ctx->num_inflight))
+               wake_up(&ctx->wait);
+}
+
+int mlx5_cmd_exec_cb(struct mlx5_async_ctx *ctx, void *in, int in_size,
+                    void *out, int out_size, mlx5_async_cbk_t callback,
+                    struct mlx5_async_work *work)
 {
-       return cmd_exec(dev, in, in_size, out, out_size, callback, context,
-                       false);
+       int ret;
+
+       work->ctx = ctx;
+       work->user_callback = callback;
+       if (WARN_ON(!atomic_inc_not_zero(&ctx->num_inflight)))
+               return -EIO;
+       ret = cmd_exec(ctx->dev, in, in_size, out, out_size,
+                      mlx5_cmd_exec_cb_handler, work, false);
+       if (ret && atomic_dec_and_test(&ctx->num_inflight))
+               wake_up(&ctx->wait);
+
+       return ret;
 }
 EXPORT_SYMBOL(mlx5_cmd_exec_cb);
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ecpf.c b/drivers/net/ethernet/mellanox/mlx5/core/ecpf.c
new file mode 100644 (file)
index 0000000..4746f2d
--- /dev/null
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2019 Mellanox Technologies. */
+
+#include "ecpf.h"
+
+bool mlx5_read_embedded_cpu(struct mlx5_core_dev *dev)
+{
+       return (ioread32be(&dev->iseg->initializing) >> MLX5_ECPU_BIT_NUM) & 1;
+}
+
+static int mlx5_peer_pf_enable_hca(struct mlx5_core_dev *dev)
+{
+       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, 0);
+       MLX5_SET(enable_hca_in, in, embedded_cpu_function, 0);
+       return mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+}
+
+static int mlx5_peer_pf_disable_hca(struct mlx5_core_dev *dev)
+{
+       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, 0);
+       MLX5_SET(enable_hca_in, in, embedded_cpu_function, 0);
+       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+}
+
+static int mlx5_peer_pf_init(struct mlx5_core_dev *dev)
+{
+       int err;
+
+       err = mlx5_peer_pf_enable_hca(dev);
+       if (err)
+               mlx5_core_err(dev, "Failed to enable peer PF HCA err(%d)\n",
+                             err);
+
+       return err;
+}
+
+static void mlx5_peer_pf_cleanup(struct mlx5_core_dev *dev)
+{
+       int err;
+
+       err = mlx5_peer_pf_disable_hca(dev);
+       if (err) {
+               mlx5_core_err(dev, "Failed to disable peer PF HCA err(%d)\n",
+                             err);
+               return;
+       }
+
+       err = mlx5_wait_for_pages(dev, &dev->priv.peer_pf_pages);
+       if (err)
+               mlx5_core_warn(dev, "Timeout reclaiming peer PF pages err(%d)\n",
+                              err);
+}
+
+int mlx5_ec_init(struct mlx5_core_dev *dev)
+{
+       int err = 0;
+
+       if (!mlx5_core_is_ecpf(dev))
+               return 0;
+
+       /* ECPF shall enable HCA for peer PF in the same way a PF
+        * does this for its VFs.
+        */
+       err = mlx5_peer_pf_init(dev);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+void mlx5_ec_cleanup(struct mlx5_core_dev *dev)
+{
+       if (!mlx5_core_is_ecpf(dev))
+               return;
+
+       mlx5_peer_pf_cleanup(dev);
+}
+
+static int mlx5_query_host_params_context(struct mlx5_core_dev *dev,
+                                         u32 *out, int outlen)
+{
+       u32 in[MLX5_ST_SZ_DW(query_host_params_in)] = {};
+
+       MLX5_SET(query_host_params_in, in, opcode,
+                MLX5_CMD_OP_QUERY_HOST_PARAMS);
+
+       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
+}
+
+int mlx5_query_host_params_num_vfs(struct mlx5_core_dev *dev, int *num_vf)
+{
+       u32 out[MLX5_ST_SZ_DW(query_host_params_out)] = {};
+       int err;
+
+       err = mlx5_query_host_params_context(dev, out, sizeof(out));
+       if (err)
+               return err;
+
+       *num_vf = MLX5_GET(query_host_params_out, out,
+                          host_params_context.host_num_of_vfs);
+       mlx5_core_dbg(dev, "host_num_of_vfs %d\n", *num_vf);
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ecpf.h b/drivers/net/ethernet/mellanox/mlx5/core/ecpf.h
new file mode 100644 (file)
index 0000000..346372d
--- /dev/null
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2019 Mellanox Technologies. */
+
+#ifndef __MLX5_ECPF_H__
+#define __MLX5_ECPF_H__
+
+#include <linux/mlx5/driver.h>
+#include "mlx5_core.h"
+
+#ifdef CONFIG_MLX5_ESWITCH
+
+enum {
+       MLX5_ECPU_BIT_NUM = 23,
+};
+
+bool mlx5_read_embedded_cpu(struct mlx5_core_dev *dev);
+int mlx5_ec_init(struct mlx5_core_dev *dev);
+void mlx5_ec_cleanup(struct mlx5_core_dev *dev);
+int mlx5_query_host_params_num_vfs(struct mlx5_core_dev *dev, int *num_vf);
+
+#else  /* CONFIG_MLX5_ESWITCH */
+
+static inline bool
+mlx5_read_embedded_cpu(struct mlx5_core_dev *dev) { return false; }
+static inline int mlx5_ec_init(struct mlx5_core_dev *dev) { return 0; }
+static inline void mlx5_ec_cleanup(struct mlx5_core_dev *dev) {}
+static inline int
+mlx5_query_host_params_num_vfs(struct mlx5_core_dev *dev, int *num_vf)
+{ return -EOPNOTSUPP; }
+
+#endif /* CONFIG_MLX5_ESWITCH */
+
+#endif /* __MLX5_ECPF_H__ */
index 6dd74ef..e9acfa9 100644 (file)
@@ -387,10 +387,7 @@ struct mlx5e_txqsq {
        struct mlx5e_channel      *channel;
        int                        txq_ix;
        u32                        rate_limit;
-       struct mlx5e_txqsq_recover {
-               struct work_struct         recover_work;
-               u64                        last_recover;
-       } recover;
+       struct work_struct         recover_work;
 } ____cacheline_aligned_in_smp;
 
 struct mlx5e_dma_info {
@@ -658,6 +655,7 @@ struct mlx5e_channel_stats {
 enum {
        MLX5E_STATE_OPENED,
        MLX5E_STATE_DESTROYING,
+       MLX5E_STATE_XDP_TX_ENABLED,
 };
 
 struct mlx5e_rqt {
@@ -683,6 +681,13 @@ struct mlx5e_rss_params {
        u8      hfunc;
 };
 
+struct mlx5e_modify_sq_param {
+       int curr_state;
+       int next_state;
+       int rl_update;
+       int rl_index;
+};
+
 struct mlx5e_priv {
        /* priv data path fields - start */
        struct mlx5e_txqsq *txq2sq[MLX5E_MAX_NUM_CHANNELS * MLX5E_MAX_NUM_TC];
@@ -738,6 +743,7 @@ struct mlx5e_priv {
 #ifdef CONFIG_MLX5_EN_TLS
        struct mlx5e_tls          *tls;
 #endif
+       struct devlink_health_reporter *tx_reporter;
 };
 
 struct mlx5e_profile {
@@ -868,6 +874,11 @@ void mlx5e_set_rq_type(struct mlx5_core_dev *mdev, struct mlx5e_params *params);
 void mlx5e_init_rq_type_params(struct mlx5_core_dev *mdev,
                               struct mlx5e_params *params);
 
+int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn,
+                   struct mlx5e_modify_sq_param *p);
+void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq);
+void mlx5e_tx_disable_queue(struct netdev_queue *txq);
+
 static inline bool mlx5e_tunnel_inner_ft_supported(struct mlx5_core_dev *mdev)
 {
        return (MLX5_CAP_ETH(mdev, tunnel_stateless_gre) &&
index 4a37713..122927f 100644 (file)
@@ -63,66 +63,168 @@ static const u32 mlx5e_link_speed[MLX5E_LINK_MODES_NUMBER] = {
        [MLX5E_50GBASE_KR2]       = 50000,
 };
 
-u32 mlx5e_port_ptys2speed(u32 eth_proto_oper)
+static const u32 mlx5e_ext_link_speed[MLX5E_EXT_LINK_MODES_NUMBER] = {
+       [MLX5E_SGMII_100M]                      = 100,
+       [MLX5E_1000BASE_X_SGMII]                = 1000,
+       [MLX5E_5GBASE_R]                        = 5000,
+       [MLX5E_10GBASE_XFI_XAUI_1]              = 10000,
+       [MLX5E_40GBASE_XLAUI_4_XLPPI_4]         = 40000,
+       [MLX5E_25GAUI_1_25GBASE_CR_KR]          = 25000,
+       [MLX5E_50GAUI_2_LAUI_2_50GBASE_CR2_KR2] = 50000,
+       [MLX5E_50GAUI_1_LAUI_1_50GBASE_CR_KR]   = 50000,
+       [MLX5E_CAUI_4_100GBASE_CR4_KR4]         = 100000,
+       [MLX5E_200GAUI_4_200GBASE_CR4_KR4]      = 200000,
+       [MLX5E_400GAUI_8]                       = 400000,
+};
+
+static void mlx5e_port_get_speed_arr(struct mlx5_core_dev *mdev,
+                                    const u32 **arr, u32 *size)
+{
+       bool ext = MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet);
+
+       *size = ext ? ARRAY_SIZE(mlx5e_ext_link_speed) :
+                     ARRAY_SIZE(mlx5e_link_speed);
+       *arr  = ext ? mlx5e_ext_link_speed : mlx5e_link_speed;
+}
+
+int mlx5_port_query_eth_proto(struct mlx5_core_dev *dev, u8 port, bool ext,
+                             struct mlx5e_port_eth_proto *eproto)
+{
+       u32 out[MLX5_ST_SZ_DW(ptys_reg)];
+       int err;
+
+       if (!eproto)
+               return -EINVAL;
+
+       if (ext !=  MLX5_CAP_PCAM_FEATURE(dev, ptys_extended_ethernet))
+               return -EOPNOTSUPP;
+
+       err = mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN, port);
+       if (err)
+               return err;
+
+       eproto->cap   = MLX5_GET_ETH_PROTO(ptys_reg, out, ext,
+                                          eth_proto_capability);
+       eproto->admin = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_admin);
+       eproto->oper  = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_oper);
+       return 0;
+}
+
+void mlx5_port_query_eth_autoneg(struct mlx5_core_dev *dev, u8 *an_status,
+                                u8 *an_disable_cap, u8 *an_disable_admin)
+{
+       u32 out[MLX5_ST_SZ_DW(ptys_reg)];
+
+       *an_status = 0;
+       *an_disable_cap = 0;
+       *an_disable_admin = 0;
+
+       if (mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN, 1))
+               return;
+
+       *an_status = MLX5_GET(ptys_reg, out, an_status);
+       *an_disable_cap = MLX5_GET(ptys_reg, out, an_disable_cap);
+       *an_disable_admin = MLX5_GET(ptys_reg, out, an_disable_admin);
+}
+
+int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable,
+                          u32 proto_admin, bool ext)
+{
+       u32 out[MLX5_ST_SZ_DW(ptys_reg)];
+       u32 in[MLX5_ST_SZ_DW(ptys_reg)];
+       u8 an_disable_admin;
+       u8 an_disable_cap;
+       u8 an_status;
+
+       mlx5_port_query_eth_autoneg(dev, &an_status, &an_disable_cap,
+                                   &an_disable_admin);
+       if (!an_disable_cap && an_disable)
+               return -EPERM;
+
+       memset(in, 0, sizeof(in));
+
+       MLX5_SET(ptys_reg, in, local_port, 1);
+       MLX5_SET(ptys_reg, in, an_disable_admin, an_disable);
+       MLX5_SET(ptys_reg, in, proto_mask, MLX5_PTYS_EN);
+       if (ext)
+               MLX5_SET(ptys_reg, in, ext_eth_proto_admin, proto_admin);
+       else
+               MLX5_SET(ptys_reg, in, eth_proto_admin, proto_admin);
+
+       return mlx5_core_access_reg(dev, in, sizeof(in), out,
+                           sizeof(out), MLX5_REG_PTYS, 0, 1);
+}
+
+u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper)
 {
        unsigned long temp = eth_proto_oper;
+       const u32 *table;
        u32 speed = 0;
+       u32 max_size;
        int i;
 
-       i = find_first_bit(&temp, MLX5E_LINK_MODES_NUMBER);
-       if (i < MLX5E_LINK_MODES_NUMBER)
-               speed = mlx5e_link_speed[i];
-
+       mlx5e_port_get_speed_arr(mdev, &table, &max_size);
+       i = find_first_bit(&temp, max_size);
+       if (i < max_size)
+               speed = table[i];
        return speed;
 }
 
 int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
 {
-       u32 out[MLX5_ST_SZ_DW(ptys_reg)] = {};
-       u32 eth_proto_oper;
+       struct mlx5e_port_eth_proto eproto;
+       bool ext;
        int err;
 
-       err = mlx5_query_port_ptys(mdev, out, sizeof(out), MLX5_PTYS_EN, 1);
+       ext = MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet);
+       err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto);
        if (err)
-               return err;
+               goto out;
 
-       eth_proto_oper = MLX5_GET(ptys_reg, out, eth_proto_oper);
-       *speed = mlx5e_port_ptys2speed(eth_proto_oper);
+       *speed = mlx5e_port_ptys2speed(mdev, eproto.oper);
        if (!(*speed))
                err = -EINVAL;
 
+out:
        return err;
 }
 
 int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
 {
+       struct mlx5e_port_eth_proto eproto;
        u32 max_speed = 0;
-       u32 proto_cap;
+       const u32 *table;
+       u32 max_size;
+       bool ext;
        int err;
        int i;
 
-       err = mlx5_query_port_proto_cap(mdev, &proto_cap, MLX5_PTYS_EN);
+       ext = MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet);
+       err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto);
        if (err)
                return err;
 
-       for (i = 0; i < MLX5E_LINK_MODES_NUMBER; ++i)
-               if (proto_cap & MLX5E_PROT_MASK(i))
-                       max_speed = max(max_speed, mlx5e_link_speed[i]);
+       mlx5e_port_get_speed_arr(mdev, &table, &max_size);
+       for (i = 0; i < max_size; ++i)
+               if (eproto.cap & MLX5E_PROT_MASK(i))
+                       max_speed = max(max_speed, table[i]);
 
        *speed = max_speed;
        return 0;
 }
 
-u32 mlx5e_port_speed2linkmodes(u32 speed)
+u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed)
 {
        u32 link_modes = 0;
+       const u32 *table;
+       u32 max_size;
        int i;
 
-       for (i = 0; i < MLX5E_LINK_MODES_NUMBER; ++i) {
-               if (mlx5e_link_speed[i] == speed)
+       mlx5e_port_get_speed_arr(mdev, &table, &max_size);
+       for (i = 0; i < max_size; ++i) {
+               if (table[i] == speed)
                        link_modes |= MLX5E_PROT_MASK(i);
        }
-
        return link_modes;
 }
 
index cd2160b..70f536e 100644 (file)
 #include <linux/mlx5/driver.h>
 #include "en.h"
 
-u32 mlx5e_port_ptys2speed(u32 eth_proto_oper);
+struct mlx5e_port_eth_proto {
+       u32 cap;
+       u32 admin;
+       u32 oper;
+};
+
+int mlx5_port_query_eth_proto(struct mlx5_core_dev *dev, u8 port, bool ext,
+                             struct mlx5e_port_eth_proto *eproto);
+void mlx5_port_query_eth_autoneg(struct mlx5_core_dev *dev, u8 *an_status,
+                                u8 *an_disable_cap, u8 *an_disable_admin);
+int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable,
+                          u32 proto_admin, bool ext);
+u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper);
 int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed);
 int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed);
-u32 mlx5e_port_speed2linkmodes(u32 speed);
+u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed);
 
 int mlx5e_port_query_pbmc(struct mlx5_core_dev *mdev, void *out);
 int mlx5e_port_set_pbmc(struct mlx5_core_dev *mdev, void *in);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter.h b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter.h
new file mode 100644 (file)
index 0000000..e78e927
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2019 Mellanox Technologies. */
+
+#ifndef __MLX5E_EN_REPORTER_H
+#define __MLX5E_EN_REPORTER_H
+
+#include <linux/mlx5/driver.h>
+#include "en.h"
+
+int mlx5e_tx_reporter_create(struct mlx5e_priv *priv);
+void mlx5e_tx_reporter_destroy(struct mlx5e_priv *priv);
+void mlx5e_tx_reporter_err_cqe(struct mlx5e_txqsq *sq);
+int mlx5e_tx_reporter_timeout(struct mlx5e_txqsq *sq);
+
+#endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
new file mode 100644 (file)
index 0000000..0aebfb3
--- /dev/null
@@ -0,0 +1,297 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2019 Mellanox Technologies. */
+
+#include <net/devlink.h>
+#include "reporter.h"
+#include "lib/eq.h"
+
+#define MLX5E_TX_REPORTER_PER_SQ_MAX_LEN 256
+
+struct mlx5e_tx_err_ctx {
+       int (*recover)(struct mlx5e_txqsq *sq);
+       struct mlx5e_txqsq *sq;
+};
+
+static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq)
+{
+       unsigned long exp_time = jiffies + msecs_to_jiffies(2000);
+
+       while (time_before(jiffies, exp_time)) {
+               if (sq->cc == sq->pc)
+                       return 0;
+
+               msleep(20);
+       }
+
+       netdev_err(sq->channel->netdev,
+                  "Wait for SQ 0x%x flush timeout (sq cc = 0x%x, sq pc = 0x%x)\n",
+                  sq->sqn, sq->cc, sq->pc);
+
+       return -ETIMEDOUT;
+}
+
+static void mlx5e_reset_txqsq_cc_pc(struct mlx5e_txqsq *sq)
+{
+       WARN_ONCE(sq->cc != sq->pc,
+                 "SQ 0x%x: cc (0x%x) != pc (0x%x)\n",
+                 sq->sqn, sq->cc, sq->pc);
+       sq->cc = 0;
+       sq->dma_fifo_cc = 0;
+       sq->pc = 0;
+}
+
+static int mlx5e_sq_to_ready(struct mlx5e_txqsq *sq, int curr_state)
+{
+       struct mlx5_core_dev *mdev = sq->channel->mdev;
+       struct net_device *dev = sq->channel->netdev;
+       struct mlx5e_modify_sq_param msp = {0};
+       int err;
+
+       msp.curr_state = curr_state;
+       msp.next_state = MLX5_SQC_STATE_RST;
+
+       err = mlx5e_modify_sq(mdev, sq->sqn, &msp);
+       if (err) {
+               netdev_err(dev, "Failed to move sq 0x%x to reset\n", sq->sqn);
+               return err;
+       }
+
+       memset(&msp, 0, sizeof(msp));
+       msp.curr_state = MLX5_SQC_STATE_RST;
+       msp.next_state = MLX5_SQC_STATE_RDY;
+
+       err = mlx5e_modify_sq(mdev, sq->sqn, &msp);
+       if (err) {
+               netdev_err(dev, "Failed to move sq 0x%x to ready\n", sq->sqn);
+               return err;
+       }
+
+       return 0;
+}
+
+static int mlx5e_tx_reporter_err_cqe_recover(struct mlx5e_txqsq *sq)
+{
+       struct mlx5_core_dev *mdev = sq->channel->mdev;
+       struct net_device *dev = sq->channel->netdev;
+       u8 state;
+       int err;
+
+       if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
+               return 0;
+
+       err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
+       if (err) {
+               netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
+                          sq->sqn, err);
+               return err;
+       }
+
+       if (state != MLX5_SQC_STATE_ERR) {
+               netdev_err(dev, "SQ 0x%x not in ERROR state\n", sq->sqn);
+               return -EINVAL;
+       }
+
+       mlx5e_tx_disable_queue(sq->txq);
+
+       err = mlx5e_wait_for_sq_flush(sq);
+       if (err)
+               return err;
+
+       /* At this point, no new packets will arrive from the stack as TXQ is
+        * marked with QUEUE_STATE_DRV_XOFF. In addition, NAPI cleared all
+        * pending WQEs. SQ can safely reset the SQ.
+        */
+
+       err = mlx5e_sq_to_ready(sq, state);
+       if (err)
+               return err;
+
+       mlx5e_reset_txqsq_cc_pc(sq);
+       sq->stats->recover++;
+       mlx5e_activate_txqsq(sq);
+
+       return 0;
+}
+
+void mlx5e_tx_reporter_err_cqe(struct mlx5e_txqsq *sq)
+{
+       char err_str[MLX5E_TX_REPORTER_PER_SQ_MAX_LEN];
+       struct mlx5e_tx_err_ctx err_ctx = {0};
+
+       err_ctx.sq       = sq;
+       err_ctx.recover  = mlx5e_tx_reporter_err_cqe_recover;
+       sprintf(err_str, "ERR CQE on SQ: 0x%x", sq->sqn);
+
+       devlink_health_report(sq->channel->priv->tx_reporter, err_str,
+                             &err_ctx);
+}
+
+static int mlx5e_tx_reporter_timeout_recover(struct mlx5e_txqsq *sq)
+{
+       struct mlx5_eq_comp *eq = sq->cq.mcq.eq;
+       u32 eqe_count;
+       int ret;
+
+       netdev_err(sq->channel->netdev, "EQ 0x%x: Cons = 0x%x, irqn = 0x%x\n",
+                  eq->core.eqn, eq->core.cons_index, eq->core.irqn);
+
+       eqe_count = mlx5_eq_poll_irq_disabled(eq);
+       ret = eqe_count ? true : false;
+       if (!eqe_count) {
+               clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
+               return ret;
+       }
+
+       netdev_err(sq->channel->netdev, "Recover %d eqes on EQ 0x%x\n",
+                  eqe_count, eq->core.eqn);
+       sq->channel->stats->eq_rearm++;
+       return ret;
+}
+
+int mlx5e_tx_reporter_timeout(struct mlx5e_txqsq *sq)
+{
+       char err_str[MLX5E_TX_REPORTER_PER_SQ_MAX_LEN];
+       struct mlx5e_tx_err_ctx err_ctx;
+
+       err_ctx.sq       = sq;
+       err_ctx.recover  = mlx5e_tx_reporter_timeout_recover;
+       sprintf(err_str,
+               "TX timeout on queue: %d, SQ: 0x%x, CQ: 0x%x, SQ Cons: 0x%x SQ Prod: 0x%x, usecs since last trans: %u\n",
+               sq->channel->ix, sq->sqn, sq->cq.mcq.cqn, sq->cc, sq->pc,
+               jiffies_to_usecs(jiffies - sq->txq->trans_start));
+
+       return devlink_health_report(sq->channel->priv->tx_reporter, err_str,
+                                    &err_ctx);
+}
+
+/* state lock cannot be grabbed within this function.
+ * It can cause a dead lock or a read-after-free.
+ */
+int mlx5e_tx_reporter_recover_from_ctx(struct mlx5e_tx_err_ctx *err_ctx)
+{
+       return err_ctx->recover(err_ctx->sq);
+}
+
+static int mlx5e_tx_reporter_recover_all(struct mlx5e_priv *priv)
+{
+       int err;
+
+       rtnl_lock();
+       mutex_lock(&priv->state_lock);
+       mlx5e_close_locked(priv->netdev);
+       err = mlx5e_open_locked(priv->netdev);
+       mutex_unlock(&priv->state_lock);
+       rtnl_unlock();
+
+       return err;
+}
+
+static int mlx5e_tx_reporter_recover(struct devlink_health_reporter *reporter,
+                                    void *context)
+{
+       struct mlx5e_priv *priv = devlink_health_reporter_priv(reporter);
+       struct mlx5e_tx_err_ctx *err_ctx = context;
+
+       return err_ctx ? mlx5e_tx_reporter_recover_from_ctx(err_ctx) :
+                        mlx5e_tx_reporter_recover_all(priv);
+}
+
+static int
+mlx5e_tx_reporter_build_diagnose_output(struct devlink_fmsg *fmsg,
+                                       u32 sqn, u8 state, bool stopped)
+{
+       int err;
+
+       err = devlink_fmsg_obj_nest_start(fmsg);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_u32_pair_put(fmsg, "sqn", sqn);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_u8_pair_put(fmsg, "HW state", state);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_bool_pair_put(fmsg, "stopped", stopped);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_obj_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int mlx5e_tx_reporter_diagnose(struct devlink_health_reporter *reporter,
+                                     struct devlink_fmsg *fmsg)
+{
+       struct mlx5e_priv *priv = devlink_health_reporter_priv(reporter);
+       int i, err = 0;
+
+       mutex_lock(&priv->state_lock);
+
+       if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
+               goto unlock;
+
+       err = devlink_fmsg_arr_pair_nest_start(fmsg, "SQs");
+       if (err)
+               goto unlock;
+
+       for (i = 0; i < priv->channels.num * priv->channels.params.num_tc;
+            i++) {
+               struct mlx5e_txqsq *sq = priv->txq2sq[i];
+               u8 state;
+
+               err = mlx5_core_query_sq_state(priv->mdev, sq->sqn, &state);
+               if (err)
+                       break;
+
+               err = mlx5e_tx_reporter_build_diagnose_output(fmsg, sq->sqn,
+                                                             state,
+                                                             netif_xmit_stopped(sq->txq));
+               if (err)
+                       break;
+       }
+       err = devlink_fmsg_arr_pair_nest_end(fmsg);
+       if (err)
+               goto unlock;
+
+unlock:
+       mutex_unlock(&priv->state_lock);
+       return err;
+}
+
+static const struct devlink_health_reporter_ops mlx5_tx_reporter_ops = {
+               .name = "tx",
+               .recover = mlx5e_tx_reporter_recover,
+               .diagnose = mlx5e_tx_reporter_diagnose,
+};
+
+#define MLX5_REPORTER_TX_GRACEFUL_PERIOD 500
+
+int mlx5e_tx_reporter_create(struct mlx5e_priv *priv)
+{
+       struct mlx5_core_dev *mdev = priv->mdev;
+       struct devlink *devlink = priv_to_devlink(mdev);
+
+       priv->tx_reporter =
+               devlink_health_reporter_create(devlink, &mlx5_tx_reporter_ops,
+                                              MLX5_REPORTER_TX_GRACEFUL_PERIOD,
+                                              true, priv);
+       if (IS_ERR_OR_NULL(priv->tx_reporter))
+               netdev_warn(priv->netdev,
+                           "Failed to create tx reporter, err = %ld\n",
+                           PTR_ERR(priv->tx_reporter));
+       return PTR_ERR_OR_ZERO(priv->tx_reporter);
+}
+
+void mlx5e_tx_reporter_destroy(struct mlx5e_priv *priv)
+{
+       if (IS_ERR_OR_NULL(priv->tx_reporter))
+               return;
+
+       devlink_health_reporter_destroy(priv->tx_reporter);
+}
index 046948e..bdcc5e7 100644 (file)
@@ -25,7 +25,7 @@ static int get_route_and_out_devs(struct mlx5e_priv *priv,
        /* if the egress device isn't on the same HW e-switch or
         * it's a LAG device, use the uplink
         */
-       if (!switchdev_port_same_parent_id(priv->netdev, dev) ||
+       if (!netdev_port_same_parent_id(priv->netdev, dev) ||
            dst_is_lag_dev) {
                *route_dev = uplink_dev;
                *out_dev = *route_dev;
@@ -256,6 +256,7 @@ int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv,
        e->m_neigh.family = n->ops->family;
        memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len);
        e->out_dev = out_dev;
+       e->route_dev = route_dev;
 
        /* It's important to add the neigh to the hash table before checking
         * the neigh validity state. So if we'll get a notification, in case the
@@ -369,6 +370,7 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
        e->m_neigh.family = n->ops->family;
        memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len);
        e->out_dev = out_dev;
+       e->route_dev = route_dev;
 
        /* It's importent to add the neigh to the hash table before checking
         * the neigh validity state. So if we'll get a notification, in case the
@@ -496,25 +498,21 @@ static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
                                    void *headers_c,
                                    void *headers_v)
 {
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
        struct netlink_ext_ack *extack = f->common.extack;
-       struct flow_dissector_key_ports *key =
-               skb_flow_dissector_target(f->dissector,
-                                         FLOW_DISSECTOR_KEY_ENC_PORTS,
-                                         f->key);
-       struct flow_dissector_key_ports *mask =
-               skb_flow_dissector_target(f->dissector,
-                                         FLOW_DISSECTOR_KEY_ENC_PORTS,
-                                         f->mask);
        void *misc_c = MLX5_ADDR_OF(fte_match_param,
                                    spec->match_criteria,
                                    misc_parameters);
        void *misc_v = MLX5_ADDR_OF(fte_match_param,
                                    spec->match_value,
                                    misc_parameters);
+       struct flow_match_ports enc_ports;
+
+       flow_rule_match_enc_ports(rule, &enc_ports);
 
        /* Full udp dst port must be given */
-       if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_PORTS) ||
-           memchr_inv(&mask->dst, 0xff, sizeof(mask->dst))) {
+       if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS) ||
+           memchr_inv(&enc_ports.mask->dst, 0xff, sizeof(enc_ports.mask->dst))) {
                NL_SET_ERR_MSG_MOD(extack,
                                   "VXLAN decap filter must include enc_dst_port condition");
                netdev_warn(priv->netdev,
@@ -523,12 +521,12 @@ static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
        }
 
        /* udp dst port must be knonwn as a VXLAN port */
-       if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, be16_to_cpu(key->dst))) {
+       if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, be16_to_cpu(enc_ports.key->dst))) {
                NL_SET_ERR_MSG_MOD(extack,
                                   "Matched UDP port is not registered as a VXLAN port");
                netdev_warn(priv->netdev,
                            "UDP port %d is not registered as a VXLAN port\n",
-                           be16_to_cpu(key->dst));
+                           be16_to_cpu(enc_ports.key->dst));
                return -EOPNOTSUPP;
        }
 
@@ -536,26 +534,26 @@ static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
        MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol);
        MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_UDP);
 
-       MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_dport, ntohs(mask->dst));
-       MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_dport, ntohs(key->dst));
+       MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_dport,
+                ntohs(enc_ports.mask->dst));
+       MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_dport,
+                ntohs(enc_ports.key->dst));
 
-       MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_sport, ntohs(mask->src));
-       MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_sport, ntohs(key->src));
+       MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_sport,
+                ntohs(enc_ports.mask->src));
+       MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_sport,
+                ntohs(enc_ports.key->src));
 
        /* match on VNI */
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
-               struct flow_dissector_key_keyid *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_KEYID,
-                                                 f->key);
-               struct flow_dissector_key_keyid *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_KEYID,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+               struct flow_match_enc_keyid enc_keyid;
+
+               flow_rule_match_enc_keyid(rule, &enc_keyid);
+
                MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
-                        be32_to_cpu(mask->keyid));
+                        be32_to_cpu(enc_keyid.mask->keyid));
                MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
-                        be32_to_cpu(key->keyid));
+                        be32_to_cpu(enc_keyid.key->keyid));
        }
        return 0;
 }
@@ -570,6 +568,7 @@ static int mlx5e_tc_tun_parse_gretap(struct mlx5e_priv *priv,
                                    misc_parameters);
        void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
                                    misc_parameters);
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
 
        if (!MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap)) {
                NL_SET_ERR_MSG_MOD(f->common.extack,
@@ -587,21 +586,14 @@ static int mlx5e_tc_tun_parse_gretap(struct mlx5e_priv *priv,
        MLX5_SET(fte_match_set_misc, misc_v, gre_protocol, ETH_P_TEB);
 
        /* gre key */
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
-               struct flow_dissector_key_keyid *mask = NULL;
-               struct flow_dissector_key_keyid *key = NULL;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+               struct flow_match_enc_keyid enc_keyid;
 
-               mask = skb_flow_dissector_target(f->dissector,
-                                                FLOW_DISSECTOR_KEY_ENC_KEYID,
-                                                f->mask);
+               flow_rule_match_enc_keyid(rule, &enc_keyid);
                MLX5_SET(fte_match_set_misc, misc_c,
-                        gre_key.key, be32_to_cpu(mask->keyid));
-
-               key = skb_flow_dissector_target(f->dissector,
-                                               FLOW_DISSECTOR_KEY_ENC_KEYID,
-                                               f->key);
+                        gre_key.key, be32_to_cpu(enc_keyid.mask->keyid));
                MLX5_SET(fte_match_set_misc, misc_v,
-                        gre_key.key, be32_to_cpu(key->keyid));
+                        gre_key.key, be32_to_cpu(enc_keyid.key->keyid));
        }
 
        return 0;
@@ -612,16 +604,18 @@ int mlx5e_tc_tun_parse(struct net_device *filter_dev,
                       struct mlx5_flow_spec *spec,
                       struct tc_cls_flower_offload *f,
                       void *headers_c,
-                      void *headers_v)
+                      void *headers_v, u8 *match_level)
 {
        int tunnel_type;
        int err = 0;
 
        tunnel_type = mlx5e_tc_tun_get_type(filter_dev);
        if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
+               *match_level = MLX5_MATCH_L4;
                err = mlx5e_tc_tun_parse_vxlan(priv, spec, f,
                                               headers_c, headers_v);
        } else if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
+               *match_level = MLX5_MATCH_L3;
                err = mlx5e_tc_tun_parse_gretap(priv, spec, f,
                                                headers_c, headers_v);
        } else {
index 706ce7b..b63f15d 100644 (file)
@@ -39,6 +39,6 @@ int mlx5e_tc_tun_parse(struct net_device *filter_dev,
                       struct mlx5_flow_spec *spec,
                       struct tc_cls_flower_offload *f,
                       void *headers_c,
-                      void *headers_v);
+                      void *headers_v, u8 *match_level);
 
 #endif //__MLX5_EN_TC_TUNNEL_H__
index 3740177..03b2a9f 100644 (file)
@@ -365,7 +365,8 @@ int mlx5e_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames,
        int sq_num;
        int i;
 
-       if (unlikely(!test_bit(MLX5E_STATE_OPENED, &priv->state)))
+       /* this flag is sufficient, no need to test internal sq state */
+       if (unlikely(!mlx5e_xdp_tx_is_enabled(priv)))
                return -ENETDOWN;
 
        if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
@@ -378,9 +379,6 @@ int mlx5e_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames,
 
        sq = &priv->channels.c[sq_num]->xdpsq;
 
-       if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state)))
-               return -ENETDOWN;
-
        for (i = 0; i < n; i++) {
                struct xdp_frame *xdpf = frames[i];
                struct mlx5e_xdp_info xdpi;
index 3a67cb3..ee27a7c 100644 (file)
@@ -50,6 +50,23 @@ void mlx5e_xdp_rx_poll_complete(struct mlx5e_rq *rq);
 int mlx5e_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames,
                   u32 flags);
 
+static inline void mlx5e_xdp_tx_enable(struct mlx5e_priv *priv)
+{
+       set_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state);
+}
+
+static inline void mlx5e_xdp_tx_disable(struct mlx5e_priv *priv)
+{
+       clear_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state);
+       /* let other device's napi(s) see our new state */
+       synchronize_rcu();
+}
+
+static inline bool mlx5e_xdp_tx_is_enabled(struct mlx5e_priv *priv)
+{
+       return test_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state);
+}
+
 static inline void mlx5e_xmit_xdp_doorbell(struct mlx5e_xdpsq *sq)
 {
        if (sq->doorbell_cseg) {
index 3bbccea..3cd7325 100644 (file)
@@ -354,9 +354,6 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv,
 
        new_channels.params = priv->channels.params;
        new_channels.params.num_channels = count;
-       if (!netif_is_rxfh_configured(priv->netdev))
-               mlx5e_build_default_indir_rqt(priv->rss_params.indirection_rqt,
-                                             MLX5E_INDIR_RQT_SIZE, count);
 
        if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
                priv->channels.params = new_channels.params;
@@ -372,6 +369,10 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv,
        if (arfs_enabled)
                mlx5e_arfs_disable(priv);
 
+       if (!netif_is_rxfh_configured(priv->netdev))
+               mlx5e_build_default_indir_rqt(priv->rss_params.indirection_rqt,
+                                             MLX5E_INDIR_RQT_SIZE, count);
+
        /* Switch to new channels, set new parameters and close old ones */
        mlx5e_switch_priv_channels(priv, &new_channels, NULL);
 
@@ -695,13 +696,14 @@ static void get_speed_duplex(struct net_device *netdev,
                             u32 eth_proto_oper,
                             struct ethtool_link_ksettings *link_ksettings)
 {
+       struct mlx5e_priv *priv = netdev_priv(netdev);
        u32 speed = SPEED_UNKNOWN;
        u8 duplex = DUPLEX_UNKNOWN;
 
        if (!netif_carrier_ok(netdev))
                goto out;
 
-       speed = mlx5e_port_ptys2speed(eth_proto_oper);
+       speed = mlx5e_port_ptys2speed(priv->mdev, eth_proto_oper);
        if (!speed) {
                speed = SPEED_UNKNOWN;
                goto out;
@@ -885,7 +887,7 @@ int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv,
                                     const struct ethtool_link_ksettings *link_ksettings)
 {
        struct mlx5_core_dev *mdev = priv->mdev;
-       u32 eth_proto_cap, eth_proto_admin;
+       struct mlx5e_port_eth_proto eproto;
        bool an_changes = false;
        u8 an_disable_admin;
        u8 an_disable_cap;
@@ -899,16 +901,16 @@ int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv,
 
        link_modes = link_ksettings->base.autoneg == AUTONEG_ENABLE ?
                mlx5e_ethtool2ptys_adver_link(link_ksettings->link_modes.advertising) :
-               mlx5e_port_speed2linkmodes(speed);
+               mlx5e_port_speed2linkmodes(mdev, speed);
 
-       err = mlx5_query_port_proto_cap(mdev, &eth_proto_cap, MLX5_PTYS_EN);
+       err = mlx5_port_query_eth_proto(mdev, 1, false, &eproto);
        if (err) {
-               netdev_err(priv->netdev, "%s: query port eth proto cap failed: %d\n",
+               netdev_err(priv->netdev, "%s: query port eth proto failed: %d\n",
                           __func__, err);
                goto out;
        }
 
-       link_modes = link_modes & eth_proto_cap;
+       link_modes = link_modes & eproto.cap;
        if (!link_modes) {
                netdev_err(priv->netdev, "%s: Not supported link mode(s) requested",
                           __func__);
@@ -916,24 +918,17 @@ int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv,
                goto out;
        }
 
-       err = mlx5_query_port_proto_admin(mdev, &eth_proto_admin, MLX5_PTYS_EN);
-       if (err) {
-               netdev_err(priv->netdev, "%s: query port eth proto admin failed: %d\n",
-                          __func__, err);
-               goto out;
-       }
-
-       mlx5_query_port_autoneg(mdev, MLX5_PTYS_EN, &an_status,
-                               &an_disable_cap, &an_disable_admin);
+       mlx5_port_query_eth_autoneg(mdev, &an_status, &an_disable_cap,
+                                   &an_disable_admin);
 
        an_disable = link_ksettings->base.autoneg == AUTONEG_DISABLE;
        an_changes = ((!an_disable && an_disable_admin) ||
                      (an_disable && !an_disable_admin));
 
-       if (!an_changes && link_modes == eth_proto_admin)
+       if (!an_changes && link_modes == eproto.admin)
                goto out;
 
-       mlx5_set_port_ptys(mdev, an_disable, link_modes, MLX5_PTYS_EN);
+       mlx5_port_set_eth_ptys(mdev, an_disable, link_modes, false);
        mlx5_toggle_port_link(mdev);
 
 out:
index 17b6bab..83510ca 100644 (file)
@@ -51,6 +51,7 @@
 #include "en/xdp.h"
 #include "lib/eq.h"
 #include "en/monitor_stats.h"
+#include "en/reporter.h"
 
 struct mlx5e_rq_param {
        u32                     rqc[MLX5_ST_SZ_DW(rqc)];
@@ -949,7 +950,7 @@ static int mlx5e_open_rq(struct mlx5e_channel *c,
        if (params->rx_dim_enabled)
                __set_bit(MLX5E_RQ_STATE_AM, &c->rq.state);
 
-       if (params->pflags & MLX5E_PFLAG_RX_NO_CSUM_COMPLETE)
+       if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_NO_CSUM_COMPLETE))
                __set_bit(MLX5E_RQ_STATE_NO_CSUM_COMPLETE, &c->rq.state);
 
        return 0;
@@ -1159,7 +1160,7 @@ static int mlx5e_alloc_txqsq_db(struct mlx5e_txqsq *sq, int numa)
        return 0;
 }
 
-static void mlx5e_sq_recover(struct work_struct *work);
+static void mlx5e_tx_err_cqe_work(struct work_struct *recover_work);
 static int mlx5e_alloc_txqsq(struct mlx5e_channel *c,
                             int txq_ix,
                             struct mlx5e_params *params,
@@ -1181,7 +1182,7 @@ 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->stats     = &c->priv->channel_stats[c->ix].sq[tc];
-       INIT_WORK(&sq->recover.recover_work, mlx5e_sq_recover);
+       INIT_WORK(&sq->recover_work, mlx5e_tx_err_cqe_work);
        if (MLX5_IPSEC_DEV(c->priv->mdev))
                set_bit(MLX5E_SQ_STATE_IPSEC, &sq->state);
        if (mlx5_accel_is_tls_device(c->priv->mdev))
@@ -1269,15 +1270,8 @@ static int mlx5e_create_sq(struct mlx5_core_dev *mdev,
        return err;
 }
 
-struct mlx5e_modify_sq_param {
-       int curr_state;
-       int next_state;
-       bool rl_update;
-       int rl_index;
-};
-
-static int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn,
-                          struct mlx5e_modify_sq_param *p)
+int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn,
+                   struct mlx5e_modify_sq_param *p)
 {
        void *in;
        void *sqc;
@@ -1375,17 +1369,7 @@ err_free_txqsq:
        return err;
 }
 
-static void mlx5e_reset_txqsq_cc_pc(struct mlx5e_txqsq *sq)
-{
-       WARN_ONCE(sq->cc != sq->pc,
-                 "SQ 0x%x: cc (0x%x) != pc (0x%x)\n",
-                 sq->sqn, sq->cc, sq->pc);
-       sq->cc = 0;
-       sq->dma_fifo_cc = 0;
-       sq->pc = 0;
-}
-
-static void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq)
+void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq)
 {
        sq->txq = netdev_get_tx_queue(sq->channel->netdev, sq->txq_ix);
        clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
@@ -1394,7 +1378,7 @@ static void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq)
        netif_tx_start_queue(sq->txq);
 }
 
-static inline void netif_tx_disable_queue(struct netdev_queue *txq)
+void mlx5e_tx_disable_queue(struct netdev_queue *txq)
 {
        __netif_tx_lock_bh(txq);
        netif_tx_stop_queue(txq);
@@ -1410,7 +1394,7 @@ static void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq)
        /* prevent netif_tx_wake_queue */
        napi_synchronize(&c->napi);
 
-       netif_tx_disable_queue(sq->txq);
+       mlx5e_tx_disable_queue(sq->txq);
 
        /* last doorbell out, godspeed .. */
        if (mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1)) {
@@ -1430,6 +1414,7 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq)
        struct mlx5_rate_limit rl = {0};
 
        cancel_work_sync(&sq->dim.work);
+       cancel_work_sync(&sq->recover_work);
        mlx5e_destroy_sq(mdev, sq->sqn);
        if (sq->rate_limit) {
                rl.rate = sq->rate_limit;
@@ -1439,105 +1424,12 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq)
        mlx5e_free_txqsq(sq);
 }
 
-static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq)
-{
-       unsigned long exp_time = jiffies + msecs_to_jiffies(2000);
-
-       while (time_before(jiffies, exp_time)) {
-               if (sq->cc == sq->pc)
-                       return 0;
-
-               msleep(20);
-       }
-
-       netdev_err(sq->channel->netdev,
-                  "Wait for SQ 0x%x flush timeout (sq cc = 0x%x, sq pc = 0x%x)\n",
-                  sq->sqn, sq->cc, sq->pc);
-
-       return -ETIMEDOUT;
-}
-
-static int mlx5e_sq_to_ready(struct mlx5e_txqsq *sq, int curr_state)
-{
-       struct mlx5_core_dev *mdev = sq->channel->mdev;
-       struct net_device *dev = sq->channel->netdev;
-       struct mlx5e_modify_sq_param msp = {0};
-       int err;
-
-       msp.curr_state = curr_state;
-       msp.next_state = MLX5_SQC_STATE_RST;
-
-       err = mlx5e_modify_sq(mdev, sq->sqn, &msp);
-       if (err) {
-               netdev_err(dev, "Failed to move sq 0x%x to reset\n", sq->sqn);
-               return err;
-       }
-
-       memset(&msp, 0, sizeof(msp));
-       msp.curr_state = MLX5_SQC_STATE_RST;
-       msp.next_state = MLX5_SQC_STATE_RDY;
-
-       err = mlx5e_modify_sq(mdev, sq->sqn, &msp);
-       if (err) {
-               netdev_err(dev, "Failed to move sq 0x%x to ready\n", sq->sqn);
-               return err;
-       }
-
-       return 0;
-}
-
-static void mlx5e_sq_recover(struct work_struct *work)
+static void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
 {
-       struct mlx5e_txqsq_recover *recover =
-               container_of(work, struct mlx5e_txqsq_recover,
-                            recover_work);
-       struct mlx5e_txqsq *sq = container_of(recover, struct mlx5e_txqsq,
-                                             recover);
-       struct mlx5_core_dev *mdev = sq->channel->mdev;
-       struct net_device *dev = sq->channel->netdev;
-       u8 state;
-       int err;
-
-       err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
-       if (err) {
-               netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
-                          sq->sqn, err);
-               return;
-       }
-
-       if (state != MLX5_RQC_STATE_ERR) {
-               netdev_err(dev, "SQ 0x%x not in ERROR state\n", sq->sqn);
-               return;
-       }
-
-       netif_tx_disable_queue(sq->txq);
-
-       if (mlx5e_wait_for_sq_flush(sq))
-               return;
-
-       /* If the interval between two consecutive recovers per SQ is too
-        * short, don't recover to avoid infinite loop of ERR_CQE -> recover.
-        * If we reached this state, there is probably a bug that needs to be
-        * fixed. let's keep the queue close and let tx timeout cleanup.
-        */
-       if (jiffies_to_msecs(jiffies - recover->last_recover) <
-           MLX5E_SQ_RECOVER_MIN_INTERVAL) {
-               netdev_err(dev, "Recover SQ 0x%x canceled, too many error CQEs\n",
-                          sq->sqn);
-               return;
-       }
-
-       /* At this point, no new packets will arrive from the stack as TXQ is
-        * marked with QUEUE_STATE_DRV_XOFF. In addition, NAPI cleared all
-        * pending WQEs.  SQ can safely reset the SQ.
-        */
-       if (mlx5e_sq_to_ready(sq, state))
-               return;
+       struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
+                                             recover_work);
 
-       mlx5e_reset_txqsq_cc_pc(sq);
-       sq->stats->recover++;
-       recover->last_recover = jiffies;
-       mlx5e_activate_txqsq(sq);
+       mlx5e_tx_reporter_err_cqe(sq);
 }
 
 static int mlx5e_open_icosq(struct mlx5e_channel *c,
@@ -2967,6 +2859,7 @@ void mlx5e_activate_priv_channels(struct mlx5e_priv *priv)
 
        mlx5e_build_tx2sq_maps(priv);
        mlx5e_activate_channels(&priv->channels);
+       mlx5e_xdp_tx_enable(priv);
        netif_tx_start_all_queues(priv->netdev);
 
        if (mlx5e_is_vport_rep(priv))
@@ -2988,6 +2881,7 @@ void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv)
         */
        netif_tx_stop_all_queues(priv->netdev);
        netif_tx_disable(priv->netdev);
+       mlx5e_xdp_tx_disable(priv);
        mlx5e_deactivate_channels(&priv->channels);
 }
 
@@ -3236,6 +3130,7 @@ static void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv)
 {
        int tc;
 
+       mlx5e_tx_reporter_destroy(priv);
        for (tc = 0; tc < priv->profile->max_tc; tc++)
                mlx5e_destroy_tis(priv->mdev, priv->tisn[tc]);
 }
@@ -4223,31 +4118,13 @@ netdev_features_t mlx5e_features_check(struct sk_buff *skb,
        return features;
 }
 
-static bool mlx5e_tx_timeout_eq_recover(struct net_device *dev,
-                                       struct mlx5e_txqsq *sq)
-{
-       struct mlx5_eq_comp *eq = sq->cq.mcq.eq;
-       u32 eqe_count;
-
-       netdev_err(dev, "EQ 0x%x: Cons = 0x%x, irqn = 0x%x\n",
-                  eq->core.eqn, eq->core.cons_index, eq->core.irqn);
-
-       eqe_count = mlx5_eq_poll_irq_disabled(eq);
-       if (!eqe_count)
-               return false;
-
-       netdev_err(dev, "Recover %d eqes on EQ 0x%x\n", eqe_count, eq->core.eqn);
-       sq->channel->stats->eq_rearm++;
-       return true;
-}
-
 static void mlx5e_tx_timeout_work(struct work_struct *work)
 {
        struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
                                               tx_timeout_work);
-       struct net_device *dev = priv->netdev;
-       bool reopen_channels = false;
-       int i, err;
+       bool report_failed = false;
+       int err;
+       int i;
 
        rtnl_lock();
        mutex_lock(&priv->state_lock);
@@ -4256,31 +4133,22 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
                goto unlock;
 
        for (i = 0; i < priv->channels.num * priv->channels.params.num_tc; i++) {
-               struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, i);
+               struct netdev_queue *dev_queue =
+                       netdev_get_tx_queue(priv->netdev, i);
                struct mlx5e_txqsq *sq = priv->txq2sq[i];
 
                if (!netif_xmit_stopped(dev_queue))
                        continue;
 
-               netdev_err(dev,
-                          "TX timeout on queue: %d, SQ: 0x%x, CQ: 0x%x, SQ Cons: 0x%x SQ Prod: 0x%x, usecs since last trans: %u\n",
-                          i, sq->sqn, sq->cq.mcq.cqn, sq->cc, sq->pc,
-                          jiffies_to_usecs(jiffies - dev_queue->trans_start));
-
-               /* If we recover a lost interrupt, most likely TX timeout will
-                * be resolved, skip reopening channels
-                */
-               if (!mlx5e_tx_timeout_eq_recover(dev, sq)) {
-                       clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
-                       reopen_channels = true;
-               }
+               if (mlx5e_tx_reporter_timeout(sq))
+                       report_failed = true;
        }
 
-       if (!reopen_channels)
+       if (!report_failed)
                goto unlock;
 
-       mlx5e_close_locked(dev);
-       err = mlx5e_open_locked(dev);
+       mlx5e_close_locked(priv->netdev);
+       err = mlx5e_open_locked(priv->netdev);
        if (err)
                netdev_err(priv->netdev,
                           "mlx5e_open_locked failed recovering from a tx_timeout, err(%d).\n",
@@ -4296,6 +4164,12 @@ static void mlx5e_tx_timeout(struct net_device *dev)
        struct mlx5e_priv *priv = netdev_priv(dev);
 
        netdev_err(dev, "TX timeout detected\n");
+
+       if (IS_ERR_OR_NULL(priv->tx_reporter)) {
+               netdev_err_once(priv->netdev, "tx timeout will not be handled, no valid tx reporter\n");
+               return;
+       }
+
        queue_work(priv->wq, &priv->tx_timeout_work);
 }
 
@@ -4953,6 +4827,7 @@ static int mlx5e_init_nic_tx(struct mlx5e_priv *priv)
 #ifdef CONFIG_MLX5_CORE_EN_DCB
        mlx5e_dcbnl_initialize(priv);
 #endif
+       mlx5e_tx_reporter_create(priv);
        return 0;
 }
 
index edb34b3..287d48e 100644 (file)
@@ -153,7 +153,7 @@ static void mlx5e_rep_update_hw_counters(struct mlx5e_priv *priv)
        struct mlx5e_rep_priv *rpriv = priv->ppriv;
        struct mlx5_eswitch_rep *rep = rpriv->rep;
 
-       if (rep->vport == FDB_UPLINK_VPORT)
+       if (rep->vport == MLX5_VPORT_UPLINK)
                mlx5e_uplink_rep_update_hw_counters(priv);
        else
                mlx5e_vf_rep_update_hw_counters(priv);
@@ -381,7 +381,8 @@ static const struct ethtool_ops mlx5e_uplink_rep_ethtool_ops = {
        .set_pauseparam    = mlx5e_uplink_rep_set_pauseparam,
 };
 
-static int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr)
+static int mlx5e_rep_get_port_parent_id(struct net_device *dev,
+                                       struct netdev_phys_item_id *ppid)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
@@ -398,20 +399,14 @@ static int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr)
                uplink_priv = netdev_priv(uplink_dev);
        }
 
-       switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = ETH_ALEN;
-               if (uplink_upper && mlx5_lag_is_sriov(uplink_priv->mdev)) {
-                       ether_addr_copy(attr->u.ppid.id, uplink_upper->dev_addr);
-               } else {
-                       struct mlx5e_rep_priv *rpriv = priv->ppriv;
-                       struct mlx5_eswitch_rep *rep = rpriv->rep;
+       ppid->id_len = ETH_ALEN;
+       if (uplink_upper && mlx5_lag_is_sriov(uplink_priv->mdev)) {
+               ether_addr_copy(ppid->id, uplink_upper->dev_addr);
+       } else {
+               struct mlx5e_rep_priv *rpriv = priv->ppriv;
+               struct mlx5_eswitch_rep *rep = rpriv->rep;
 
-                       ether_addr_copy(attr->u.ppid.id, rep->hw_id);
-               }
-               break;
-       default:
-               return -EOPNOTSUPP;
+               ether_addr_copy(ppid->id, rep->hw_id);
        }
 
        return 0;
@@ -584,6 +579,10 @@ static void mlx5e_rep_update_flows(struct mlx5e_priv *priv,
        if (neigh_connected && !(e->flags & MLX5_ENCAP_ENTRY_VALID)) {
                ether_addr_copy(e->h_dest, ha);
                ether_addr_copy(eth->h_dest, ha);
+               /* Update the encap source mac, in case that we delete
+                * the flows when encap source mac changed.
+                */
+               ether_addr_copy(eth->h_source, e->route_dev->dev_addr);
 
                mlx5e_tc_encap_flows_add(priv, e);
        }
@@ -1084,7 +1083,8 @@ static int mlx5e_vf_rep_open(struct net_device *dev)
 
        if (!mlx5_modify_vport_admin_state(priv->mdev,
                                           MLX5_VPORT_STATE_OP_MOD_ESW_VPORT,
-                                          rep->vport, MLX5_VPORT_ADMIN_STATE_UP))
+                                          rep->vport, 1,
+                                          MLX5_VPORT_ADMIN_STATE_UP))
                netif_carrier_on(dev);
 
 unlock:
@@ -1102,7 +1102,8 @@ static int mlx5e_vf_rep_close(struct net_device *dev)
        mutex_lock(&priv->state_lock);
        mlx5_modify_vport_admin_state(priv->mdev,
                                      MLX5_VPORT_STATE_OP_MOD_ESW_VPORT,
-                                     rep->vport, MLX5_VPORT_ADMIN_STATE_DOWN);
+                                     rep->vport, 1,
+                                     MLX5_VPORT_ADMIN_STATE_DOWN);
        ret = mlx5e_close_locked(dev);
        mutex_unlock(&priv->state_lock);
        return ret;
@@ -1114,9 +1115,17 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev,
        struct mlx5e_priv *priv = netdev_priv(dev);
        struct mlx5e_rep_priv *rpriv = priv->ppriv;
        struct mlx5_eswitch_rep *rep = rpriv->rep;
-       int ret;
+       int ret, pf_num;
+
+       ret = mlx5_lag_get_pf_num(priv->mdev, &pf_num);
+       if (ret)
+               return ret;
+
+       if (rep->vport == MLX5_VPORT_UPLINK)
+               ret = snprintf(buf, len, "p%d", pf_num);
+       else
+               ret = snprintf(buf, len, "pf%dvf%d", pf_num, rep->vport - 1);
 
-       ret = snprintf(buf, len, "%d", rep->vport - 1);
        if (ret >= len)
                return -EOPNOTSUPP;
 
@@ -1199,7 +1208,7 @@ bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv)
                return false;
 
        rep = rpriv->rep;
-       return (rep->vport == FDB_UPLINK_VPORT);
+       return (rep->vport == MLX5_VPORT_UPLINK);
 }
 
 static bool mlx5e_rep_has_offload_stats(const struct net_device *dev, int attr_id)
@@ -1264,9 +1273,17 @@ static int mlx5e_uplink_rep_set_mac(struct net_device *netdev, void *addr)
        return 0;
 }
 
-static const struct switchdev_ops mlx5e_rep_switchdev_ops = {
-       .switchdev_port_attr_get        = mlx5e_attr_get,
-};
+static int mlx5e_uplink_rep_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos,
+                                       __be16 vlan_proto)
+{
+       netdev_warn_once(dev, "legacy vf vlan setting isn't supported in switchdev mode\n");
+
+       if (vlan != 0)
+               return -EOPNOTSUPP;
+
+       /* allow setting 0-vid for compatibility with libvirt */
+       return 0;
+}
 
 static const struct net_device_ops mlx5e_netdev_ops_vf_rep = {
        .ndo_open                = mlx5e_vf_rep_open,
@@ -1278,6 +1295,7 @@ static const struct net_device_ops mlx5e_netdev_ops_vf_rep = {
        .ndo_has_offload_stats   = mlx5e_rep_has_offload_stats,
        .ndo_get_offload_stats   = mlx5e_rep_get_offload_stats,
        .ndo_change_mtu          = mlx5e_vf_rep_change_mtu,
+       .ndo_get_port_parent_id  = mlx5e_rep_get_port_parent_id,
 };
 
 static const struct net_device_ops mlx5e_netdev_ops_uplink_rep = {
@@ -1298,6 +1316,8 @@ static const struct net_device_ops mlx5e_netdev_ops_uplink_rep = {
        .ndo_set_vf_rate         = mlx5e_set_vf_rate,
        .ndo_get_vf_config       = mlx5e_get_vf_config,
        .ndo_get_vf_stats        = mlx5e_get_vf_stats,
+       .ndo_set_vf_vlan         = mlx5e_uplink_rep_set_vf_vlan,
+       .ndo_get_port_parent_id  = mlx5e_rep_get_port_parent_id,
 };
 
 bool mlx5e_eswitch_rep(struct net_device *netdev)
@@ -1326,7 +1346,7 @@ static void mlx5e_build_rep_params(struct net_device *netdev)
        params->sw_mtu      = netdev->mtu;
 
        /* SQ */
-       if (rep->vport == FDB_UPLINK_VPORT)
+       if (rep->vport == MLX5_VPORT_UPLINK)
                params->log_sq_size = MLX5E_PARAMS_DEFAULT_LOG_SQ_SIZE;
        else
                params->log_sq_size = MLX5E_REP_PARAMS_DEF_LOG_SQ_SIZE;
@@ -1353,7 +1373,7 @@ static void mlx5e_build_rep_netdev(struct net_device *netdev)
        struct mlx5_eswitch_rep *rep = rpriv->rep;
        struct mlx5_core_dev *mdev = priv->mdev;
 
-       if (rep->vport == FDB_UPLINK_VPORT) {
+       if (rep->vport == MLX5_VPORT_UPLINK) {
                SET_NETDEV_DEV(netdev, &priv->mdev->pdev->dev);
                netdev->netdev_ops = &mlx5e_netdev_ops_uplink_rep;
                /* we want a persistent mac for the uplink rep */
@@ -1372,8 +1392,6 @@ static void mlx5e_build_rep_netdev(struct net_device *netdev)
        netdev->watchdog_timeo    = 15 * HZ;
 
 
-       netdev->switchdev_ops = &mlx5e_rep_switchdev_ops;
-
        netdev->features         |= NETIF_F_HW_TC | NETIF_F_NETNS_LOCAL;
        netdev->hw_features      |= NETIF_F_HW_TC;
 
@@ -1385,7 +1403,7 @@ static void mlx5e_build_rep_netdev(struct net_device *netdev)
        netdev->hw_features    |= NETIF_F_TSO6;
        netdev->hw_features    |= NETIF_F_RXCSUM;
 
-       if (rep->vport != FDB_UPLINK_VPORT)
+       if (rep->vport != MLX5_VPORT_UPLINK)
                netdev->features |= NETIF_F_VLAN_CHALLENGED;
 
        netdev->features |= netdev->hw_features;
@@ -1538,7 +1556,7 @@ static int mlx5e_init_rep_tx(struct mlx5e_priv *priv)
                return err;
        }
 
-       if (rpriv->rep->vport == FDB_UPLINK_VPORT) {
+       if (rpriv->rep->vport == MLX5_VPORT_UPLINK) {
                uplink_priv = &rpriv->uplink_priv;
 
                /* init shared tc flow table */
@@ -1574,7 +1592,7 @@ static void mlx5e_cleanup_rep_tx(struct mlx5e_priv *priv)
        for (tc = 0; tc < priv->profile->max_tc; tc++)
                mlx5e_destroy_tis(priv->mdev, priv->tisn[tc]);
 
-       if (rpriv->rep->vport == FDB_UPLINK_VPORT) {
+       if (rpriv->rep->vport == MLX5_VPORT_UPLINK) {
                /* clean indirect TC block notifications */
                unregister_netdevice_notifier(&rpriv->uplink_priv.netdevice_nb);
                mlx5e_rep_indr_clean_block_privs(rpriv);
@@ -1693,7 +1711,7 @@ mlx5e_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
        rpriv->rep = rep;
 
        nch = mlx5e_get_max_num_channels(dev);
-       profile = (rep->vport == FDB_UPLINK_VPORT) ? &mlx5e_uplink_rep_profile : &mlx5e_vf_rep_profile;
+       profile = (rep->vport == MLX5_VPORT_UPLINK) ? &mlx5e_uplink_rep_profile : &mlx5e_vf_rep_profile;
        netdev = mlx5e_create_netdev(dev, profile, nch, rpriv);
        if (!netdev) {
                pr_warn("Failed to create representor netdev for vport %d\n",
@@ -1706,7 +1724,7 @@ mlx5e_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
        rep->rep_if[REP_ETH].priv = rpriv;
        INIT_LIST_HEAD(&rpriv->vport_sqs_list);
 
-       if (rep->vport == FDB_UPLINK_VPORT) {
+       if (rep->vport == MLX5_VPORT_UPLINK) {
                err = mlx5e_create_mdev_resources(dev);
                if (err)
                        goto err_destroy_netdev;
@@ -1742,7 +1760,7 @@ err_detach_netdev:
        mlx5e_detach_netdev(netdev_priv(netdev));
 
 err_destroy_mdev_resources:
-       if (rep->vport == FDB_UPLINK_VPORT)
+       if (rep->vport == MLX5_VPORT_UPLINK)
                mlx5e_destroy_mdev_resources(dev);
 
 err_destroy_netdev:
@@ -1762,7 +1780,7 @@ mlx5e_vport_rep_unload(struct mlx5_eswitch_rep *rep)
        unregister_netdev(netdev);
        mlx5e_rep_neigh_cleanup(rpriv);
        mlx5e_detach_netdev(priv);
-       if (rep->vport == FDB_UPLINK_VPORT)
+       if (rep->vport == MLX5_VPORT_UPLINK)
                mlx5e_destroy_mdev_resources(priv->mdev);
        mlx5e_destroy_netdev(priv);
        kfree(ppriv); /* mlx5e_rep_priv */
@@ -1780,25 +1798,18 @@ static void *mlx5e_vport_rep_get_proto_dev(struct mlx5_eswitch_rep *rep)
 void mlx5e_rep_register_vport_reps(struct mlx5_core_dev *mdev)
 {
        struct mlx5_eswitch *esw = mdev->priv.eswitch;
-       int total_vfs = MLX5_TOTAL_VPORTS(mdev);
-       int vport;
+       struct mlx5_eswitch_rep_if rep_if = {};
 
-       for (vport = 0; vport < total_vfs; vport++) {
-               struct mlx5_eswitch_rep_if rep_if = {};
+       rep_if.load = mlx5e_vport_rep_load;
+       rep_if.unload = mlx5e_vport_rep_unload;
+       rep_if.get_proto_dev = mlx5e_vport_rep_get_proto_dev;
 
-               rep_if.load = mlx5e_vport_rep_load;
-               rep_if.unload = mlx5e_vport_rep_unload;
-               rep_if.get_proto_dev = mlx5e_vport_rep_get_proto_dev;
-               mlx5_eswitch_register_vport_rep(esw, vport, &rep_if, REP_ETH);
-       }
+       mlx5_eswitch_register_vport_reps(esw, &rep_if, REP_ETH);
 }
 
 void mlx5e_rep_unregister_vport_reps(struct mlx5_core_dev *mdev)
 {
        struct mlx5_eswitch *esw = mdev->priv.eswitch;
-       int total_vfs = MLX5_TOTAL_VPORTS(mdev);
-       int vport;
 
-       for (vport = total_vfs - 1; vport >= 0; vport--)
-               mlx5_eswitch_unregister_vport_rep(esw, vport, REP_ETH);
+       mlx5_eswitch_unregister_vport_reps(esw, REP_ETH);
 }
index edd7228..36eafc8 100644 (file)
@@ -148,6 +148,7 @@ struct mlx5e_encap_entry {
        unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
 
        struct net_device *out_dev;
+       struct net_device *route_dev;
        int tunnel_type;
        int tunnel_hlen;
        int reformat_type;
index 74159d3..05892fc 100644 (file)
@@ -38,7 +38,6 @@
 #include <linux/mlx5/fs.h>
 #include <linux/mlx5/device.h>
 #include <linux/rhashtable.h>
-#include <net/switchdev.h>
 #include <net/tc_act/tc_mirred.h>
 #include <net/tc_act/tc_vlan.h>
 #include <net/tc_act/tc_tunnel_key.h>
@@ -128,6 +127,7 @@ struct mlx5e_tc_flow_parse_attr {
        struct net_device *filter_dev;
        struct mlx5_flow_spec spec;
        int num_mod_hdr_actions;
+       int max_mod_hdr_actions;
        void *mod_hdr_actions;
        int mirred_ifindex[MLX5_MAX_FLOW_FWD_VPORTS];
 };
@@ -1302,101 +1302,89 @@ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
 static int parse_tunnel_attr(struct mlx5e_priv *priv,
                             struct mlx5_flow_spec *spec,
                             struct tc_cls_flower_offload *f,
-                            struct net_device *filter_dev)
+                            struct net_device *filter_dev, u8 *match_level)
 {
        struct netlink_ext_ack *extack = f->common.extack;
        void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
                                       outer_headers);
        void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
                                       outer_headers);
-
-       struct flow_dissector_key_control *enc_control =
-               skb_flow_dissector_target(f->dissector,
-                                         FLOW_DISSECTOR_KEY_ENC_CONTROL,
-                                         f->key);
-       int err = 0;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
+       struct flow_match_control enc_control;
+       int err;
 
        err = mlx5e_tc_tun_parse(filter_dev, priv, spec, f,
-                                headers_c, headers_v);
+                                headers_c, headers_v, match_level);
        if (err) {
                NL_SET_ERR_MSG_MOD(extack,
                                   "failed to parse tunnel attributes");
                return err;
        }
 
-       if (enc_control->addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
-               struct flow_dissector_key_ipv4_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS,
-                                                 f->key);
-               struct flow_dissector_key_ipv4_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS,
-                                                 f->mask);
+       flow_rule_match_enc_control(rule, &enc_control);
+
+       if (enc_control.key->addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+               struct flow_match_ipv4_addrs match;
+
+               flow_rule_match_enc_ipv4_addrs(rule, &match);
                MLX5_SET(fte_match_set_lyr_2_4, headers_c,
                         src_ipv4_src_ipv6.ipv4_layout.ipv4,
-                        ntohl(mask->src));
+                        ntohl(match.mask->src));
                MLX5_SET(fte_match_set_lyr_2_4, headers_v,
                         src_ipv4_src_ipv6.ipv4_layout.ipv4,
-                        ntohl(key->src));
+                        ntohl(match.key->src));
 
                MLX5_SET(fte_match_set_lyr_2_4, headers_c,
                         dst_ipv4_dst_ipv6.ipv4_layout.ipv4,
-                        ntohl(mask->dst));
+                        ntohl(match.mask->dst));
                MLX5_SET(fte_match_set_lyr_2_4, headers_v,
                         dst_ipv4_dst_ipv6.ipv4_layout.ipv4,
-                        ntohl(key->dst));
+                        ntohl(match.key->dst));
 
                MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ethertype);
                MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype, ETH_P_IP);
-       } else if (enc_control->addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
-               struct flow_dissector_key_ipv6_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS,
-                                                 f->key);
-               struct flow_dissector_key_ipv6_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS,
-                                                 f->mask);
+       } else if (enc_control.key->addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+               struct flow_match_ipv6_addrs match;
 
+               flow_rule_match_enc_ipv6_addrs(rule, &match);
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
                                    src_ipv4_src_ipv6.ipv6_layout.ipv6),
-                      &mask->src, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
+                      &match.mask->src, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
                                    src_ipv4_src_ipv6.ipv6_layout.ipv6),
-                      &key->src, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
+                      &match.key->src, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
 
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
                                    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
-                      &mask->dst, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
+                      &match.mask->dst, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
                                    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
-                      &key->dst, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
+                      &match.key->dst, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
 
                MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ethertype);
                MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype, ETH_P_IPV6);
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_IP)) {
-               struct flow_dissector_key_ip *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_IP,
-                                                 f->key);
-               struct flow_dissector_key_ip *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_IP,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IP)) {
+               struct flow_match_ip match;
 
-               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn, mask->tos & 0x3);
-               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn, key->tos & 0x3);
+               flow_rule_match_enc_ip(rule, &match);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn,
+                        match.mask->tos & 0x3);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn,
+                        match.key->tos & 0x3);
 
-               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_dscp, mask->tos >> 2);
-               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_dscp, key->tos  >> 2);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_dscp,
+                        match.mask->tos >> 2);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_dscp,
+                        match.key->tos  >> 2);
 
-               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ttl_hoplimit, mask->ttl);
-               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ttl_hoplimit, key->ttl);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ttl_hoplimit,
+                        match.mask->ttl);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ttl_hoplimit,
+                        match.key->ttl);
 
-               if (mask->ttl &&
+               if (match.mask->ttl &&
                    !MLX5_CAP_ESW_FLOWTABLE_FDB
                        (priv->mdev,
                         ft_field_support.outer_ipv4_ttl)) {
@@ -1426,7 +1414,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                              struct mlx5_flow_spec *spec,
                              struct tc_cls_flower_offload *f,
                              struct net_device *filter_dev,
-                             u8 *match_level)
+                             u8 *match_level, u8 *tunnel_match_level)
 {
        struct netlink_ext_ack *extack = f->common.extack;
        void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
@@ -1437,12 +1425,14 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                                    misc_parameters);
        void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
                                    misc_parameters);
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
+       struct flow_dissector *dissector = rule->match.dissector;
        u16 addr_type = 0;
        u8 ip_proto = 0;
 
        *match_level = MLX5_MATCH_NONE;
 
-       if (f->dissector->used_keys &
+       if (dissector->used_keys &
            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
              BIT(FLOW_DISSECTOR_KEY_BASIC) |
              BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
@@ -1461,23 +1451,21 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
              BIT(FLOW_DISSECTOR_KEY_ENC_IP))) {
                NL_SET_ERR_MSG_MOD(extack, "Unsupported key");
                netdev_warn(priv->netdev, "Unsupported key used: 0x%x\n",
-                           f->dissector->used_keys);
+                           dissector->used_keys);
                return -EOPNOTSUPP;
        }
 
-       if ((dissector_uses_key(f->dissector,
-                               FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) ||
-            dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID) ||
-            dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_PORTS)) &&
-           dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_CONTROL)) {
-               struct flow_dissector_key_control *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_CONTROL,
-                                                 f->key);
-               switch (key->addr_type) {
+       if ((flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) ||
+            flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID) ||
+            flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS)) &&
+           flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL)) {
+               struct flow_match_control match;
+
+               flow_rule_match_enc_control(rule, &match);
+               switch (match.key->addr_type) {
                case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
                case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
-                       if (parse_tunnel_attr(priv, spec, f, filter_dev))
+                       if (parse_tunnel_attr(priv, spec, f, filter_dev, tunnel_match_level))
                                return -EOPNOTSUPP;
                        break;
                default:
@@ -1493,35 +1481,27 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                                         inner_headers);
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->key);
-               struct flow_dissector_key_basic *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
+
+               flow_rule_match_basic(rule, &match);
                MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype,
-                        ntohs(mask->n_proto));
+                        ntohs(match.mask->n_proto));
                MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype,
-                        ntohs(key->n_proto));
+                        ntohs(match.key->n_proto));
 
-               if (mask->n_proto)
+               if (match.mask->n_proto)
                        *match_level = MLX5_MATCH_L2;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
-               struct flow_dissector_key_vlan *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_VLAN,
-                                                 f->key);
-               struct flow_dissector_key_vlan *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_VLAN,
-                                                 f->mask);
-               if (mask->vlan_id || mask->vlan_priority || mask->vlan_tpid) {
-                       if (key->vlan_tpid == htons(ETH_P_8021AD)) {
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+               struct flow_match_vlan match;
+
+               flow_rule_match_vlan(rule, &match);
+               if (match.mask->vlan_id ||
+                   match.mask->vlan_priority ||
+                   match.mask->vlan_tpid) {
+                       if (match.key->vlan_tpid == htons(ETH_P_8021AD)) {
                                MLX5_SET(fte_match_set_lyr_2_4, headers_c,
                                         svlan_tag, 1);
                                MLX5_SET(fte_match_set_lyr_2_4, headers_v,
@@ -1533,11 +1513,15 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                                         cvlan_tag, 1);
                        }
 
-                       MLX5_SET(fte_match_set_lyr_2_4, headers_c, first_vid, mask->vlan_id);
-                       MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_vid, key->vlan_id);
+                       MLX5_SET(fte_match_set_lyr_2_4, headers_c, first_vid,
+                                match.mask->vlan_id);
+                       MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_vid,
+                                match.key->vlan_id);
 
-                       MLX5_SET(fte_match_set_lyr_2_4, headers_c, first_prio, mask->vlan_priority);
-                       MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_prio, key->vlan_priority);
+                       MLX5_SET(fte_match_set_lyr_2_4, headers_c, first_prio,
+                                match.mask->vlan_priority);
+                       MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_prio,
+                                match.key->vlan_priority);
 
                        *match_level = MLX5_MATCH_L2;
                }
@@ -1547,17 +1531,14 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                *match_level = MLX5_MATCH_L2;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CVLAN)) {
-               struct flow_dissector_key_vlan *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_CVLAN,
-                                                 f->key);
-               struct flow_dissector_key_vlan *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_CVLAN,
-                                                 f->mask);
-               if (mask->vlan_id || mask->vlan_priority || mask->vlan_tpid) {
-                       if (key->vlan_tpid == htons(ETH_P_8021AD)) {
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CVLAN)) {
+               struct flow_match_vlan match;
+
+               flow_rule_match_vlan(rule, &match);
+               if (match.mask->vlan_id ||
+                   match.mask->vlan_priority ||
+                   match.mask->vlan_tpid) {
+                       if (match.key->vlan_tpid == htons(ETH_P_8021AD)) {
                                MLX5_SET(fte_match_set_misc, misc_c,
                                         outer_second_svlan_tag, 1);
                                MLX5_SET(fte_match_set_misc, misc_v,
@@ -1570,69 +1551,58 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                        }
 
                        MLX5_SET(fte_match_set_misc, misc_c, outer_second_vid,
-                                mask->vlan_id);
+                                match.mask->vlan_id);
                        MLX5_SET(fte_match_set_misc, misc_v, outer_second_vid,
-                                key->vlan_id);
+                                match.key->vlan_id);
                        MLX5_SET(fte_match_set_misc, misc_c, outer_second_prio,
-                                mask->vlan_priority);
+                                match.mask->vlan_priority);
                        MLX5_SET(fte_match_set_misc, misc_v, outer_second_prio,
-                                key->vlan_priority);
+                                match.key->vlan_priority);
 
                        *match_level = MLX5_MATCH_L2;
                }
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
-               struct flow_dissector_key_eth_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
-                                                 f->key);
-               struct flow_dissector_key_eth_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct flow_match_eth_addrs match;
 
+               flow_rule_match_eth_addrs(rule, &match);
                ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
                                             dmac_47_16),
-                               mask->dst);
+                               match.mask->dst);
                ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
                                             dmac_47_16),
-                               key->dst);
+                               match.key->dst);
 
                ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
                                             smac_47_16),
-                               mask->src);
+                               match.mask->src);
                ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
                                             smac_47_16),
-                               key->src);
+                               match.key->src);
 
-               if (!is_zero_ether_addr(mask->src) || !is_zero_ether_addr(mask->dst))
+               if (!is_zero_ether_addr(match.mask->src) ||
+                   !is_zero_ether_addr(match.mask->dst))
                        *match_level = MLX5_MATCH_L2;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
-               struct flow_dissector_key_control *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_CONTROL,
-                                                 f->key);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+               struct flow_match_control match;
 
-               struct flow_dissector_key_control *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_CONTROL,
-                                                 f->mask);
-               addr_type = key->addr_type;
+               flow_rule_match_control(rule, &match);
+               addr_type = match.key->addr_type;
 
                /* the HW doesn't support frag first/later */
-               if (mask->flags & FLOW_DIS_FIRST_FRAG)
+               if (match.mask->flags & FLOW_DIS_FIRST_FRAG)
                        return -EOPNOTSUPP;
 
-               if (mask->flags & FLOW_DIS_IS_FRAGMENT) {
+               if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {
                        MLX5_SET(fte_match_set_lyr_2_4, headers_c, frag, 1);
                        MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag,
-                                key->flags & FLOW_DIS_IS_FRAGMENT);
+                                match.key->flags & FLOW_DIS_IS_FRAGMENT);
 
                        /* the HW doesn't need L3 inline to match on frag=no */
-                       if (!(key->flags & FLOW_DIS_IS_FRAGMENT))
+                       if (!(match.key->flags & FLOW_DIS_IS_FRAGMENT))
                                *match_level = MLX5_MATCH_L2;
        /* ***  L2 attributes parsing up to here *** */
                        else
@@ -1640,102 +1610,85 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                }
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->key);
-               struct flow_dissector_key_basic *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->mask);
-               ip_proto = key->ip_proto;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
+
+               flow_rule_match_basic(rule, &match);
+               ip_proto = match.key->ip_proto;
 
                MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol,
-                        mask->ip_proto);
+                        match.mask->ip_proto);
                MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
-                        key->ip_proto);
+                        match.key->ip_proto);
 
-               if (mask->ip_proto)
+               if (match.mask->ip_proto)
                        *match_level = MLX5_MATCH_L3;
        }
 
        if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
-               struct flow_dissector_key_ipv4_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                                 f->key);
-               struct flow_dissector_key_ipv4_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                                 f->mask);
+               struct flow_match_ipv4_addrs match;
 
+               flow_rule_match_ipv4_addrs(rule, &match);
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
                                    src_ipv4_src_ipv6.ipv4_layout.ipv4),
-                      &mask->src, sizeof(mask->src));
+                      &match.mask->src, sizeof(match.mask->src));
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
                                    src_ipv4_src_ipv6.ipv4_layout.ipv4),
-                      &key->src, sizeof(key->src));
+                      &match.key->src, sizeof(match.key->src));
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
                                    dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
-                      &mask->dst, sizeof(mask->dst));
+                      &match.mask->dst, sizeof(match.mask->dst));
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
                                    dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
-                      &key->dst, sizeof(key->dst));
+                      &match.key->dst, sizeof(match.key->dst));
 
-               if (mask->src || mask->dst)
+               if (match.mask->src || match.mask->dst)
                        *match_level = MLX5_MATCH_L3;
        }
 
        if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
-               struct flow_dissector_key_ipv6_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                                 f->key);
-               struct flow_dissector_key_ipv6_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                                 f->mask);
+               struct flow_match_ipv6_addrs match;
 
+               flow_rule_match_ipv6_addrs(rule, &match);
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
                                    src_ipv4_src_ipv6.ipv6_layout.ipv6),
-                      &mask->src, sizeof(mask->src));
+                      &match.mask->src, sizeof(match.mask->src));
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
                                    src_ipv4_src_ipv6.ipv6_layout.ipv6),
-                      &key->src, sizeof(key->src));
+                      &match.key->src, sizeof(match.key->src));
 
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
                                    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
-                      &mask->dst, sizeof(mask->dst));
+                      &match.mask->dst, sizeof(match.mask->dst));
                memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
                                    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
-                      &key->dst, sizeof(key->dst));
+                      &match.key->dst, sizeof(match.key->dst));
 
-               if (ipv6_addr_type(&mask->src) != IPV6_ADDR_ANY ||
-                   ipv6_addr_type(&mask->dst) != IPV6_ADDR_ANY)
+               if (ipv6_addr_type(&match.mask->src) != IPV6_ADDR_ANY ||
+                   ipv6_addr_type(&match.mask->dst) != IPV6_ADDR_ANY)
                        *match_level = MLX5_MATCH_L3;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IP)) {
-               struct flow_dissector_key_ip *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IP,
-                                                 f->key);
-               struct flow_dissector_key_ip *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_IP,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
+               struct flow_match_ip match;
 
-               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn, mask->tos & 0x3);
-               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn, key->tos & 0x3);
+               flow_rule_match_ip(rule, &match);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn,
+                        match.mask->tos & 0x3);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn,
+                        match.key->tos & 0x3);
 
-               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_dscp, mask->tos >> 2);
-               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_dscp, key->tos  >> 2);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_dscp,
+                        match.mask->tos >> 2);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_dscp,
+                        match.key->tos  >> 2);
 
-               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ttl_hoplimit, mask->ttl);
-               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ttl_hoplimit, key->ttl);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ttl_hoplimit,
+                        match.mask->ttl);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ttl_hoplimit,
+                        match.key->ttl);
 
-               if (mask->ttl &&
+               if (match.mask->ttl &&
                    !MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev,
                                                ft_field_support.outer_ipv4_ttl)) {
                        NL_SET_ERR_MSG_MOD(extack,
@@ -1743,44 +1696,39 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                        return -EOPNOTSUPP;
                }
 
-               if (mask->tos || mask->ttl)
+               if (match.mask->tos || match.mask->ttl)
                        *match_level = MLX5_MATCH_L3;
        }
 
        /* ***  L3 attributes parsing up to here *** */
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
-               struct flow_dissector_key_ports *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_PORTS,
-                                                 f->key);
-               struct flow_dissector_key_ports *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_PORTS,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+               struct flow_match_ports match;
+
+               flow_rule_match_ports(rule, &match);
                switch (ip_proto) {
                case IPPROTO_TCP:
                        MLX5_SET(fte_match_set_lyr_2_4, headers_c,
-                                tcp_sport, ntohs(mask->src));
+                                tcp_sport, ntohs(match.mask->src));
                        MLX5_SET(fte_match_set_lyr_2_4, headers_v,
-                                tcp_sport, ntohs(key->src));
+                                tcp_sport, ntohs(match.key->src));
 
                        MLX5_SET(fte_match_set_lyr_2_4, headers_c,
-                                tcp_dport, ntohs(mask->dst));
+                                tcp_dport, ntohs(match.mask->dst));
                        MLX5_SET(fte_match_set_lyr_2_4, headers_v,
-                                tcp_dport, ntohs(key->dst));
+                                tcp_dport, ntohs(match.key->dst));
                        break;
 
                case IPPROTO_UDP:
                        MLX5_SET(fte_match_set_lyr_2_4, headers_c,
-                                udp_sport, ntohs(mask->src));
+                                udp_sport, ntohs(match.mask->src));
                        MLX5_SET(fte_match_set_lyr_2_4, headers_v,
-                                udp_sport, ntohs(key->src));
+                                udp_sport, ntohs(match.key->src));
 
                        MLX5_SET(fte_match_set_lyr_2_4, headers_c,
-                                udp_dport, ntohs(mask->dst));
+                                udp_dport, ntohs(match.mask->dst));
                        MLX5_SET(fte_match_set_lyr_2_4, headers_v,
-                                udp_dport, ntohs(key->dst));
+                                udp_dport, ntohs(match.key->dst));
                        break;
                default:
                        NL_SET_ERR_MSG_MOD(extack,
@@ -1790,26 +1738,20 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                        return -EINVAL;
                }
 
-               if (mask->src || mask->dst)
+               if (match.mask->src || match.mask->dst)
                        *match_level = MLX5_MATCH_L4;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_TCP)) {
-               struct flow_dissector_key_tcp *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_TCP,
-                                                 f->key);
-               struct flow_dissector_key_tcp *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_TCP,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) {
+               struct flow_match_tcp match;
 
+               flow_rule_match_tcp(rule, &match);
                MLX5_SET(fte_match_set_lyr_2_4, headers_c, tcp_flags,
-                        ntohs(mask->flags));
+                        ntohs(match.mask->flags));
                MLX5_SET(fte_match_set_lyr_2_4, headers_v, tcp_flags,
-                        ntohs(key->flags));
+                        ntohs(match.key->flags));
 
-               if (mask->flags)
+               if (match.mask->flags)
                        *match_level = MLX5_MATCH_L4;
        }
 
@@ -1826,15 +1768,15 @@ static int parse_cls_flower(struct mlx5e_priv *priv,
        struct mlx5_core_dev *dev = priv->mdev;
        struct mlx5_eswitch *esw = dev->priv.eswitch;
        struct mlx5e_rep_priv *rpriv = priv->ppriv;
+       u8 match_level, tunnel_match_level = MLX5_MATCH_NONE;
        struct mlx5_eswitch_rep *rep;
-       u8 match_level;
        int err;
 
-       err = __parse_cls_flower(priv, spec, f, filter_dev, &match_level);
+       err = __parse_cls_flower(priv, spec, f, filter_dev, &match_level, &tunnel_match_level);
 
        if (!err && (flow->flags & MLX5E_TC_FLOW_ESWITCH)) {
                rep = rpriv->rep;
-               if (rep->vport != FDB_UPLINK_VPORT &&
+               if (rep->vport != MLX5_VPORT_UPLINK &&
                    (esw->offloads.inline_mode != MLX5_INLINE_MODE_NONE &&
                    esw->offloads.inline_mode < match_level)) {
                        NL_SET_ERR_MSG_MOD(extack,
@@ -1846,10 +1788,12 @@ static int parse_cls_flower(struct mlx5e_priv *priv,
                }
        }
 
-       if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
+       if (flow->flags & MLX5E_TC_FLOW_ESWITCH) {
                flow->esw_attr->match_level = match_level;
-       else
+               flow->esw_attr->tunnel_match_level = tunnel_match_level;
+       } else {
                flow->nic_attr->match_level = match_level;
+       }
 
        return err;
 }
@@ -1862,27 +1806,32 @@ struct pedit_headers {
        struct udphdr  udp;
 };
 
+struct pedit_headers_action {
+       struct pedit_headers    vals;
+       struct pedit_headers    masks;
+       u32                     pedits;
+};
+
 static int pedit_header_offsets[] = {
-       [TCA_PEDIT_KEY_EX_HDR_TYPE_ETH] = offsetof(struct pedit_headers, eth),
-       [TCA_PEDIT_KEY_EX_HDR_TYPE_IP4] = offsetof(struct pedit_headers, ip4),
-       [TCA_PEDIT_KEY_EX_HDR_TYPE_IP6] = offsetof(struct pedit_headers, ip6),
-       [TCA_PEDIT_KEY_EX_HDR_TYPE_TCP] = offsetof(struct pedit_headers, tcp),
-       [TCA_PEDIT_KEY_EX_HDR_TYPE_UDP] = offsetof(struct pedit_headers, udp),
+       [FLOW_ACT_MANGLE_HDR_TYPE_ETH] = offsetof(struct pedit_headers, eth),
+       [FLOW_ACT_MANGLE_HDR_TYPE_IP4] = offsetof(struct pedit_headers, ip4),
+       [FLOW_ACT_MANGLE_HDR_TYPE_IP6] = offsetof(struct pedit_headers, ip6),
+       [FLOW_ACT_MANGLE_HDR_TYPE_TCP] = offsetof(struct pedit_headers, tcp),
+       [FLOW_ACT_MANGLE_HDR_TYPE_UDP] = offsetof(struct pedit_headers, udp),
 };
 
 #define pedit_header(_ph, _htype) ((void *)(_ph) + pedit_header_offsets[_htype])
 
 static int set_pedit_val(u8 hdr_type, u32 mask, u32 val, u32 offset,
-                        struct pedit_headers *masks,
-                        struct pedit_headers *vals)
+                        struct pedit_headers_action *hdrs)
 {
        u32 *curr_pmask, *curr_pval;
 
-       if (hdr_type >= __PEDIT_HDR_TYPE_MAX)
+       if (hdr_type >= 2)
                goto out_err;
 
-       curr_pmask = (u32 *)(pedit_header(masks, hdr_type) + offset);
-       curr_pval  = (u32 *)(pedit_header(vals, hdr_type) + offset);
+       curr_pmask = (u32 *)(pedit_header(&hdrs->masks, hdr_type) + offset);
+       curr_pval  = (u32 *)(pedit_header(&hdrs->vals, hdr_type) + offset);
 
        if (*curr_pmask & mask)  /* disallow acting twice on the same location */
                goto out_err;
@@ -1934,12 +1883,11 @@ static struct mlx5_fields fields[] = {
        OFFLOAD(UDP_DPORT, 2, udp.dest,   0),
 };
 
-/* On input attr->num_mod_hdr_actions tells how many HW actions can be parsed at
- * max from the SW pedit action. On success, it says how many HW actions were
- * actually parsed.
+/* On input attr->max_mod_hdr_actions tells how many HW actions can be parsed at
+ * max from the SW pedit action. On success, attr->num_mod_hdr_actions
+ * says how many HW actions were actually parsed.
  */
-static int offload_pedit_fields(struct pedit_headers *masks,
-                               struct pedit_headers *vals,
+static int offload_pedit_fields(struct pedit_headers_action *hdrs,
                                struct mlx5e_tc_flow_parse_attr *parse_attr,
                                struct netlink_ext_ack *extack)
 {
@@ -1954,15 +1902,17 @@ static int offload_pedit_fields(struct pedit_headers *masks,
        __be16 mask_be16;
        void *action;
 
-       set_masks = &masks[TCA_PEDIT_KEY_EX_CMD_SET];
-       add_masks = &masks[TCA_PEDIT_KEY_EX_CMD_ADD];
-       set_vals = &vals[TCA_PEDIT_KEY_EX_CMD_SET];
-       add_vals = &vals[TCA_PEDIT_KEY_EX_CMD_ADD];
+       set_masks = &hdrs[0].masks;
+       add_masks = &hdrs[1].masks;
+       set_vals = &hdrs[0].vals;
+       add_vals = &hdrs[1].vals;
 
        action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto);
-       action = parse_attr->mod_hdr_actions;
-       max_actions = parse_attr->num_mod_hdr_actions;
-       nactions = 0;
+       action = parse_attr->mod_hdr_actions +
+                parse_attr->num_mod_hdr_actions * action_size;
+
+       max_actions = parse_attr->max_mod_hdr_actions;
+       nactions = parse_attr->num_mod_hdr_actions;
 
        for (i = 0; i < ARRAY_SIZE(fields); i++) {
                f = &fields[i];
@@ -2053,12 +2003,14 @@ static int offload_pedit_fields(struct pedit_headers *masks,
 }
 
 static int alloc_mod_hdr_actions(struct mlx5e_priv *priv,
-                                const struct tc_action *a, int namespace,
+                                struct pedit_headers_action *hdrs,
+                                int namespace,
                                 struct mlx5e_tc_flow_parse_attr *parse_attr)
 {
        int nkeys, action_size, max_actions;
 
-       nkeys = tcf_pedit_nkeys(a);
+       nkeys = hdrs[TCA_PEDIT_KEY_EX_CMD_SET].pedits +
+               hdrs[TCA_PEDIT_KEY_EX_CMD_ADD].pedits;
        action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto);
 
        if (namespace == MLX5_FLOW_NAMESPACE_FDB) /* FDB offloading */
@@ -2073,62 +2025,67 @@ static int alloc_mod_hdr_actions(struct mlx5e_priv *priv,
        if (!parse_attr->mod_hdr_actions)
                return -ENOMEM;
 
-       parse_attr->num_mod_hdr_actions = max_actions;
+       parse_attr->max_mod_hdr_actions = max_actions;
        return 0;
 }
 
 static const struct pedit_headers zero_masks = {};
 
 static int parse_tc_pedit_action(struct mlx5e_priv *priv,
-                                const struct tc_action *a, int namespace,
+                                const struct flow_action_entry *act, int namespace,
                                 struct mlx5e_tc_flow_parse_attr *parse_attr,
+                                struct pedit_headers_action *hdrs,
                                 struct netlink_ext_ack *extack)
 {
-       struct pedit_headers masks[__PEDIT_CMD_MAX], vals[__PEDIT_CMD_MAX], *cmd_masks;
-       int nkeys, i, err = -EOPNOTSUPP;
+       u8 cmd = (act->id == FLOW_ACTION_MANGLE) ? 0 : 1;
+       int err = -EOPNOTSUPP;
        u32 mask, val, offset;
-       u8 cmd, htype;
+       u8 htype;
 
-       nkeys = tcf_pedit_nkeys(a);
+       htype = act->mangle.htype;
+       err = -EOPNOTSUPP; /* can't be all optimistic */
 
-       memset(masks, 0, sizeof(struct pedit_headers) * __PEDIT_CMD_MAX);
-       memset(vals,  0, sizeof(struct pedit_headers) * __PEDIT_CMD_MAX);
+       if (htype == FLOW_ACT_MANGLE_UNSPEC) {
+               NL_SET_ERR_MSG_MOD(extack, "legacy pedit isn't offloaded");
+               goto out_err;
+       }
 
-       for (i = 0; i < nkeys; i++) {
-               htype = tcf_pedit_htype(a, i);
-               cmd = tcf_pedit_cmd(a, i);
-               err = -EOPNOTSUPP; /* can't be all optimistic */
+       mask = act->mangle.mask;
+       val = act->mangle.val;
+       offset = act->mangle.offset;
 
-               if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
-                       NL_SET_ERR_MSG_MOD(extack,
-                                          "legacy pedit isn't offloaded");
-                       goto out_err;
-               }
+       err = set_pedit_val(htype, ~mask, val, offset, &hdrs[cmd]);
+       if (err)
+               goto out_err;
 
-               if (cmd != TCA_PEDIT_KEY_EX_CMD_SET && cmd != TCA_PEDIT_KEY_EX_CMD_ADD) {
-                       NL_SET_ERR_MSG_MOD(extack, "pedit cmd isn't offloaded");
-                       goto out_err;
-               }
+       hdrs[cmd].pedits++;
 
-               mask = tcf_pedit_mask(a, i);
-               val = tcf_pedit_val(a, i);
-               offset = tcf_pedit_offset(a, i);
+       return 0;
+out_err:
+       return err;
+}
+
+static int alloc_tc_pedit_action(struct mlx5e_priv *priv, int namespace,
+                                struct mlx5e_tc_flow_parse_attr *parse_attr,
+                                struct pedit_headers_action *hdrs,
+                                struct netlink_ext_ack *extack)
+{
+       struct pedit_headers *cmd_masks;
+       int err;
+       u8 cmd;
 
-               err = set_pedit_val(htype, ~mask, val, offset, &masks[cmd], &vals[cmd]);
+       if (!parse_attr->mod_hdr_actions) {
+               err = alloc_mod_hdr_actions(priv, hdrs, namespace, parse_attr);
                if (err)
                        goto out_err;
        }
 
-       err = alloc_mod_hdr_actions(priv, a, namespace, parse_attr);
-       if (err)
-               goto out_err;
-
-       err = offload_pedit_fields(masks, vals, parse_attr, extack);
+       err = offload_pedit_fields(hdrs, parse_attr, extack);
        if (err < 0)
                goto out_dealloc_parsed_actions;
 
        for (cmd = 0; cmd < __PEDIT_CMD_MAX; cmd++) {
-               cmd_masks = &masks[cmd];
+               cmd_masks = &hdrs[cmd].masks;
                if (memcmp(cmd_masks, &zero_masks, sizeof(zero_masks))) {
                        NL_SET_ERR_MSG_MOD(extack,
                                           "attempt to offload an unsupported field");
@@ -2178,17 +2135,22 @@ static bool csum_offload_supported(struct mlx5e_priv *priv,
 }
 
 static bool modify_header_match_supported(struct mlx5_flow_spec *spec,
-                                         struct tcf_exts *exts,
+                                         struct flow_action *flow_action,
+                                         u32 actions,
                                          struct netlink_ext_ack *extack)
 {
-       const struct tc_action *a;
+       const struct flow_action_entry *act;
        bool modify_ip_header;
        u8 htype, ip_proto;
        void *headers_v;
        u16 ethertype;
-       int nkeys, i;
+       int i;
+
+       if (actions & MLX5_FLOW_CONTEXT_ACTION_DECAP)
+               headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, inner_headers);
+       else
+               headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, outer_headers);
 
-       headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, outer_headers);
        ethertype = MLX5_GET(fte_match_set_lyr_2_4, headers_v, ethertype);
 
        /* for non-IP we only re-write MACs, so we're okay */
@@ -2196,20 +2158,16 @@ static bool modify_header_match_supported(struct mlx5_flow_spec *spec,
                goto out_ok;
 
        modify_ip_header = false;
-       tcf_exts_for_each_action(i, a, exts) {
-               int k;
-
-               if (!is_tcf_pedit(a))
+       flow_action_for_each(i, act, flow_action) {
+               if (act->id != FLOW_ACTION_MANGLE &&
+                   act->id != FLOW_ACTION_ADD)
                        continue;
 
-               nkeys = tcf_pedit_nkeys(a);
-               for (k = 0; k < nkeys; k++) {
-                       htype = tcf_pedit_htype(a, k);
-                       if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_IP4 ||
-                           htype == TCA_PEDIT_KEY_EX_HDR_TYPE_IP6) {
-                               modify_ip_header = true;
-                               break;
-                       }
+               htype = act->mangle.htype;
+               if (htype == FLOW_ACT_MANGLE_HDR_TYPE_IP4 ||
+                   htype == FLOW_ACT_MANGLE_HDR_TYPE_IP6) {
+                       modify_ip_header = true;
+                       break;
                }
        }
 
@@ -2227,7 +2185,7 @@ out_ok:
 }
 
 static bool actions_match_supported(struct mlx5e_priv *priv,
-                                   struct tcf_exts *exts,
+                                   struct flow_action *flow_action,
                                    struct mlx5e_tc_flow_parse_attr *parse_attr,
                                    struct mlx5e_tc_flow *flow,
                                    struct netlink_ext_ack *extack)
@@ -2244,7 +2202,8 @@ static bool actions_match_supported(struct mlx5e_priv *priv,
                return false;
 
        if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
-               return modify_header_match_supported(&parse_attr->spec, exts,
+               return modify_header_match_supported(&parse_attr->spec,
+                                                    flow_action, actions,
                                                     extack);
 
        return true;
@@ -2264,52 +2223,50 @@ static bool same_hw_devs(struct mlx5e_priv *priv, struct mlx5e_priv *peer_priv)
        return (fsystem_guid == psystem_guid);
 }
 
-static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
+static int parse_tc_nic_actions(struct mlx5e_priv *priv,
+                               struct flow_action *flow_action,
                                struct mlx5e_tc_flow_parse_attr *parse_attr,
                                struct mlx5e_tc_flow *flow,
                                struct netlink_ext_ack *extack)
 {
        struct mlx5_nic_flow_attr *attr = flow->nic_attr;
-       const struct tc_action *a;
+       struct pedit_headers_action hdrs[2] = {};
+       const struct flow_action_entry *act;
        u32 action = 0;
        int err, i;
 
-       if (!tcf_exts_has_actions(exts))
+       if (!flow_action_has_entries(flow_action))
                return -EINVAL;
 
        attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG;
 
-       tcf_exts_for_each_action(i, a, exts) {
-               if (is_tcf_gact_shot(a)) {
+       flow_action_for_each(i, act, flow_action) {
+               switch (act->id) {
+               case FLOW_ACTION_DROP:
                        action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
                        if (MLX5_CAP_FLOWTABLE(priv->mdev,
                                               flow_table_properties_nic_receive.flow_counter))
                                action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
-                       continue;
-               }
-
-               if (is_tcf_pedit(a)) {
-                       err = parse_tc_pedit_action(priv, a, MLX5_FLOW_NAMESPACE_KERNEL,
-                                                   parse_attr, extack);
+                       break;
+               case FLOW_ACTION_MANGLE:
+               case FLOW_ACTION_ADD:
+                       err = parse_tc_pedit_action(priv, act, MLX5_FLOW_NAMESPACE_KERNEL,
+                                                   parse_attr, hdrs, extack);
                        if (err)
                                return err;
 
                        action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR |
                                  MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
-                       continue;
-               }
-
-               if (is_tcf_csum(a)) {
+                       break;
+               case FLOW_ACTION_CSUM:
                        if (csum_offload_supported(priv, action,
-                                                  tcf_csum_update_flags(a),
+                                                  act->csum_flags,
                                                   extack))
-                               continue;
+                               break;
 
                        return -EOPNOTSUPP;
-               }
-
-               if (is_tcf_mirred_egress_redirect(a)) {
-                       struct net_device *peer_dev = tcf_mirred_dev(a);
+               case FLOW_ACTION_REDIRECT: {
+                       struct net_device *peer_dev = act->dev;
 
                        if (priv->netdev->netdev_ops == peer_dev->netdev_ops &&
                            same_hw_devs(priv, netdev_priv(peer_dev))) {
@@ -2324,11 +2281,10 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                                            peer_dev->name);
                                return -EINVAL;
                        }
-                       continue;
-               }
-
-               if (is_tcf_skbedit_mark(a)) {
-                       u32 mark = tcf_skbedit_mark(a);
+                       }
+                       break;
+               case FLOW_ACTION_MARK: {
+                       u32 mark = act->mark;
 
                        if (mark & ~MLX5E_TC_FLOW_ID_MASK) {
                                NL_SET_ERR_MSG_MOD(extack,
@@ -2338,14 +2294,23 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
 
                        attr->flow_tag = mark;
                        action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
-                       continue;
+                       }
+                       break;
+               default:
+                       return -EINVAL;
                }
+       }
 
-               return -EINVAL;
+       if (hdrs[TCA_PEDIT_KEY_EX_CMD_SET].pedits ||
+           hdrs[TCA_PEDIT_KEY_EX_CMD_ADD].pedits) {
+               err = alloc_tc_pedit_action(priv, MLX5_FLOW_NAMESPACE_KERNEL,
+                                           parse_attr, hdrs, extack);
+               if (err)
+                       return err;
        }
 
        attr->action = action;
-       if (!actions_match_supported(priv, exts, parse_attr, flow, extack))
+       if (!actions_match_supported(priv, flow_action, parse_attr, flow, extack))
                return -EOPNOTSUPP;
 
        return 0;
@@ -2450,7 +2415,7 @@ out_err:
 }
 
 static int parse_tc_vlan_action(struct mlx5e_priv *priv,
-                               const struct tc_action *a,
+                               const struct flow_action_entry *act,
                                struct mlx5_esw_flow_attr *attr,
                                u32 *action)
 {
@@ -2459,7 +2424,8 @@ static int parse_tc_vlan_action(struct mlx5e_priv *priv,
        if (vlan_idx >= MLX5_FS_VLAN_DEPTH)
                return -EOPNOTSUPP;
 
-       if (tcf_vlan_action(a) == TCA_VLAN_ACT_POP) {
+       switch (act->id) {
+       case FLOW_ACTION_VLAN_POP:
                if (vlan_idx) {
                        if (!mlx5_eswitch_vlan_actions_supported(priv->mdev,
                                                                 MLX5_FS_VLAN_DEPTH))
@@ -2469,10 +2435,11 @@ static int parse_tc_vlan_action(struct mlx5e_priv *priv,
                } else {
                        *action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
                }
-       } else if (tcf_vlan_action(a) == TCA_VLAN_ACT_PUSH) {
-               attr->vlan_vid[vlan_idx] = tcf_vlan_push_vid(a);
-               attr->vlan_prio[vlan_idx] = tcf_vlan_push_prio(a);
-               attr->vlan_proto[vlan_idx] = tcf_vlan_push_proto(a);
+               break;
+       case FLOW_ACTION_VLAN_PUSH:
+               attr->vlan_vid[vlan_idx] = act->vlan.vid;
+               attr->vlan_prio[vlan_idx] = act->vlan.prio;
+               attr->vlan_proto[vlan_idx] = act->vlan.proto;
                if (!attr->vlan_proto[vlan_idx])
                        attr->vlan_proto[vlan_idx] = htons(ETH_P_8021Q);
 
@@ -2484,13 +2451,15 @@ static int parse_tc_vlan_action(struct mlx5e_priv *priv,
                        *action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2;
                } else {
                        if (!mlx5_eswitch_vlan_actions_supported(priv->mdev, 1) &&
-                           (tcf_vlan_push_proto(a) != htons(ETH_P_8021Q) ||
-                            tcf_vlan_push_prio(a)))
+                           (act->vlan.proto != htons(ETH_P_8021Q) ||
+                            act->vlan.prio))
                                return -EOPNOTSUPP;
 
                        *action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH;
                }
-       } else { /* action is TCA_VLAN_ACT_MODIFY */
+               break;
+       default:
+               /* action is FLOW_ACT_VLAN_MANGLE */
                return -EOPNOTSUPP;
        }
 
@@ -2499,58 +2468,56 @@ static int parse_tc_vlan_action(struct mlx5e_priv *priv,
        return 0;
 }
 
-static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
+static int parse_tc_fdb_actions(struct mlx5e_priv *priv,
+                               struct flow_action *flow_action,
                                struct mlx5e_tc_flow_parse_attr *parse_attr,
                                struct mlx5e_tc_flow *flow,
                                struct netlink_ext_ack *extack)
 {
+       struct pedit_headers_action hdrs[2] = {};
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        struct mlx5_esw_flow_attr *attr = flow->esw_attr;
        struct mlx5e_rep_priv *rpriv = priv->ppriv;
-       struct ip_tunnel_info *info = NULL;
-       const struct tc_action *a;
+       const struct ip_tunnel_info *info = NULL;
+       const struct flow_action_entry *act;
        bool encap = false;
        u32 action = 0;
        int err, i;
 
-       if (!tcf_exts_has_actions(exts))
+       if (!flow_action_has_entries(flow_action))
                return -EINVAL;
 
        attr->in_rep = rpriv->rep;
        attr->in_mdev = priv->mdev;
 
-       tcf_exts_for_each_action(i, a, exts) {
-               if (is_tcf_gact_shot(a)) {
+       flow_action_for_each(i, act, flow_action) {
+               switch (act->id) {
+               case FLOW_ACTION_DROP:
                        action |= MLX5_FLOW_CONTEXT_ACTION_DROP |
                                  MLX5_FLOW_CONTEXT_ACTION_COUNT;
-                       continue;
-               }
-
-               if (is_tcf_pedit(a)) {
-                       err = parse_tc_pedit_action(priv, a, MLX5_FLOW_NAMESPACE_FDB,
-                                                   parse_attr, extack);
+                       break;
+               case FLOW_ACTION_MANGLE:
+               case FLOW_ACTION_ADD:
+                       err = parse_tc_pedit_action(priv, act, MLX5_FLOW_NAMESPACE_FDB,
+                                                   parse_attr, hdrs, extack);
                        if (err)
                                return err;
 
                        action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
                        attr->split_count = attr->out_count;
-                       continue;
-               }
-
-               if (is_tcf_csum(a)) {
+                       break;
+               case FLOW_ACTION_CSUM:
                        if (csum_offload_supported(priv, action,
-                                                  tcf_csum_update_flags(a),
-                                                  extack))
-                               continue;
+                                                  act->csum_flags, extack))
+                               break;
 
                        return -EOPNOTSUPP;
-               }
-
-               if (is_tcf_mirred_egress_redirect(a) || is_tcf_mirred_egress_mirror(a)) {
+               case FLOW_ACTION_REDIRECT:
+               case FLOW_ACTION_MIRRED: {
                        struct mlx5e_priv *out_priv;
                        struct net_device *out_dev;
 
-                       out_dev = tcf_mirred_dev(a);
+                       out_dev = act->dev;
                        if (!out_dev) {
                                /* out_dev is NULL when filters with
                                 * non-existing mirred device are replayed to
@@ -2569,8 +2536,8 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
 
                        action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
                                  MLX5_FLOW_CONTEXT_ACTION_COUNT;
-                       if (switchdev_port_same_parent_id(priv->netdev,
-                                                         out_dev) ||
+                       if (netdev_port_same_parent_id(priv->netdev,
+                                                      out_dev) ||
                            is_merged_eswitch_dev(priv, out_dev)) {
                                struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
                                struct net_device *uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH);
@@ -2615,35 +2582,29 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                                       priv->netdev->name, out_dev->name);
                                return -EINVAL;
                        }
-                       continue;
-               }
-
-               if (is_tcf_tunnel_set(a)) {
-                       info = tcf_tunnel_info(a);
+                       }
+                       break;
+               case FLOW_ACTION_TUNNEL_ENCAP:
+                       info = act->tunnel;
                        if (info)
                                encap = true;
                        else
                                return -EOPNOTSUPP;
-                       continue;
-               }
-
-               if (is_tcf_vlan(a)) {
-                       err = parse_tc_vlan_action(priv, a, attr, &action);
 
+                       break;
+               case FLOW_ACTION_VLAN_PUSH:
+               case FLOW_ACTION_VLAN_POP:
+                       err = parse_tc_vlan_action(priv, act, attr, &action);
                        if (err)
                                return err;
 
                        attr->split_count = attr->out_count;
-                       continue;
-               }
-
-               if (is_tcf_tunnel_release(a)) {
+                       break;
+               case FLOW_ACTION_TUNNEL_DECAP:
                        action |= MLX5_FLOW_CONTEXT_ACTION_DECAP;
-                       continue;
-               }
-
-               if (is_tcf_gact_goto_chain(a)) {
-                       u32 dest_chain = tcf_gact_goto_chain_index(a);
+                       break;
+               case FLOW_ACTION_GOTO: {
+                       u32 dest_chain = act->chain_index;
                        u32 max_chain = mlx5_eswitch_get_chain_range(esw);
 
                        if (dest_chain <= attr->chain) {
@@ -2656,15 +2617,23 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                        }
                        action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
                        attr->dest_chain = dest_chain;
-
-                       continue;
+                       break;
+                       }
+               default:
+                       return -EINVAL;
                }
+       }
 
-               return -EINVAL;
+       if (hdrs[TCA_PEDIT_KEY_EX_CMD_SET].pedits ||
+           hdrs[TCA_PEDIT_KEY_EX_CMD_ADD].pedits) {
+               err = alloc_tc_pedit_action(priv, MLX5_FLOW_NAMESPACE_KERNEL,
+                                           parse_attr, hdrs, extack);
+               if (err)
+                       return err;
        }
 
        attr->action = action;
-       if (!actions_match_supported(priv, exts, parse_attr, flow, extack))
+       if (!actions_match_supported(priv, flow_action, parse_attr, flow, extack))
                return -EOPNOTSUPP;
 
        if (attr->dest_chain) {
@@ -2724,7 +2693,7 @@ static struct rhashtable *get_tc_ht(struct mlx5e_priv *priv, int flags)
 static bool is_peer_flow_needed(struct mlx5e_tc_flow *flow)
 {
        struct mlx5_esw_flow_attr *attr = flow->esw_attr;
-       bool is_rep_ingress = attr->in_rep->vport != FDB_UPLINK_VPORT &&
+       bool is_rep_ingress = attr->in_rep->vport != MLX5_VPORT_UPLINK &&
                              flow->flags & MLX5E_TC_FLOW_INGRESS;
        bool act_is_encap = !!(attr->action &
                               MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT);
@@ -2775,6 +2744,7 @@ __mlx5e_add_fdb_flow(struct mlx5e_priv *priv,
                     struct mlx5_eswitch_rep *in_rep,
                     struct mlx5_core_dev *in_mdev)
 {
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
        struct netlink_ext_ack *extack = f->common.extack;
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        struct mlx5e_tc_flow_parse_attr *parse_attr;
@@ -2796,7 +2766,7 @@ __mlx5e_add_fdb_flow(struct mlx5e_priv *priv,
 
        flow->esw_attr->chain = f->common.chain_index;
        flow->esw_attr->prio = TC_H_MAJ(f->common.prio) >> 16;
-       err = parse_tc_fdb_actions(priv, f->exts, parse_attr, flow, extack);
+       err = parse_tc_fdb_actions(priv, &rule->action, parse_attr, flow, extack);
        if (err)
                goto err_free;
 
@@ -2846,7 +2816,7 @@ static int mlx5e_tc_add_fdb_peer_flow(struct tc_cls_flower_offload *f,
         * original flow and packets redirected from uplink use the
         * peer mdev.
         */
-       if (flow->esw_attr->in_rep->vport == FDB_UPLINK_VPORT)
+       if (flow->esw_attr->in_rep->vport == MLX5_VPORT_UPLINK)
                in_mdev = peer_priv->mdev;
        else
                in_mdev = priv->mdev;
@@ -2912,6 +2882,7 @@ mlx5e_add_nic_flow(struct mlx5e_priv *priv,
                   struct net_device *filter_dev,
                   struct mlx5e_tc_flow **__flow)
 {
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
        struct netlink_ext_ack *extack = f->common.extack;
        struct mlx5e_tc_flow_parse_attr *parse_attr;
        struct mlx5e_tc_flow *flow;
@@ -2934,7 +2905,7 @@ mlx5e_add_nic_flow(struct mlx5e_priv *priv,
        if (err)
                goto err_free;
 
-       err = parse_tc_nic_actions(priv, f->exts, parse_attr, flow, extack);
+       err = parse_tc_nic_actions(priv, &rule->action, parse_attr, flow, extack);
        if (err)
                goto err_free;
 
@@ -3092,7 +3063,7 @@ int mlx5e_stats_flower(struct net_device *dev, struct mlx5e_priv *priv,
        mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
 
 out:
-       tcf_exts_stats_update(f->exts, bytes, packets, lastuse);
+       flow_stats_update(&f->stats, bytes, packets, lastuse);
 
        return 0;
 }
index 598ad7e..c1334a8 100644 (file)
@@ -387,8 +387,14 @@ netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb,
        num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS);
        contig_wqebbs_room = mlx5_wq_cyc_get_contig_wqebbs(wq, pi);
        if (unlikely(contig_wqebbs_room < num_wqebbs)) {
+#ifdef CONFIG_MLX5_EN_IPSEC
+               struct mlx5_wqe_eth_seg cur_eth = wqe->eth;
+#endif
                mlx5e_fill_sq_frag_edge(sq, wq, pi, contig_wqebbs_room);
                mlx5e_sq_fetch_wqe(sq, &wqe, &pi);
+#ifdef CONFIG_MLX5_EN_IPSEC
+               wqe->eth = cur_eth;
+#endif
        }
 
        /* fill wqe */
@@ -513,8 +519,9 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
                                              &sq->state)) {
                                mlx5e_dump_error_cqe(sq,
                                                     (struct mlx5_err_cqe *)cqe);
-                               queue_work(cq->channel->priv->wq,
-                                          &sq->recover.recover_work);
+                               if (!IS_ERR_OR_NULL(cq->channel->priv->tx_reporter))
+                                       queue_work(cq->channel->priv->wq,
+                                                  &sq->recover_work);
                        }
                        stats->cqe_err++;
                }
index ee04aab..bb6e5b5 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/notifier.h>
 #include <linux/module.h>
 #include <linux/mlx5/driver.h>
+#include <linux/mlx5/vport.h>
 #include <linux/mlx5/eq.h>
 #include <linux/mlx5/cmd.h>
 #ifdef CONFIG_RFS_ACCEL
@@ -114,11 +115,11 @@ static struct mlx5_core_cq *mlx5_eq_cq_get(struct mlx5_eq *eq, u32 cqn)
        struct mlx5_cq_table *table = &eq->cq_table;
        struct mlx5_core_cq *cq = NULL;
 
-       spin_lock(&table->lock);
+       rcu_read_lock();
        cq = radix_tree_lookup(&table->tree, cqn);
        if (likely(cq))
                mlx5_cq_hold(cq);
-       spin_unlock(&table->lock);
+       rcu_read_unlock();
 
        return cq;
 }
@@ -371,9 +372,9 @@ int mlx5_eq_add_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq)
        struct mlx5_cq_table *table = &eq->cq_table;
        int err;
 
-       spin_lock_irq(&table->lock);
+       spin_lock(&table->lock);
        err = radix_tree_insert(&table->tree, cq->cqn, cq);
-       spin_unlock_irq(&table->lock);
+       spin_unlock(&table->lock);
 
        return err;
 }
@@ -383,9 +384,9 @@ int mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq)
        struct mlx5_cq_table *table = &eq->cq_table;
        struct mlx5_core_cq *tmp;
 
-       spin_lock_irq(&table->lock);
+       spin_lock(&table->lock);
        tmp = radix_tree_delete(&table->tree, cq->cqn);
-       spin_unlock_irq(&table->lock);
+       spin_unlock(&table->lock);
 
        if (!tmp) {
                mlx5_core_warn(eq->dev, "cq 0x%x not found in eq 0x%x tree\n", eq->eqn, cq->cqn);
@@ -530,6 +531,9 @@ static u64 gather_async_events_mask(struct mlx5_core_dev *dev)
        if (MLX5_CAP_GEN(dev, max_num_of_monitor_counters))
                async_event_mask |= (1ull << MLX5_EVENT_TYPE_MONITOR_COUNTER);
 
+       if (mlx5_core_is_ecpf_esw_manager(dev))
+               async_event_mask |= (1ull << MLX5_EVENT_TYPE_HOST_PARAMS_CHANGE);
+
        return async_event_mask;
 }
 
index a44ea7b..e18af31 100644 (file)
@@ -39,8 +39,7 @@
 #include "lib/eq.h"
 #include "eswitch.h"
 #include "fs_core.h"
-
-#define UPLINK_VPORT 0xFFFF
+#include "ecpf.h"
 
 enum {
        MLX5_ACTION_NONE = 0,
@@ -52,7 +51,7 @@ enum {
 struct vport_addr {
        struct l2addr_node     node;
        u8                     action;
-       u32                    vport;
+       u16                    vport;
        struct mlx5_flow_handle *flow_rule;
        bool mpfs; /* UC MAC was added to MPFs */
        /* A flag indicating that mac was added due to mc promiscuous vport */
@@ -70,6 +69,28 @@ enum {
                            MC_ADDR_CHANGE | \
                            PROMISC_CHANGE)
 
+/* The vport getter/iterator are only valid after esw->total_vports
+ * and vport->vport are initialized in mlx5_eswitch_init.
+ */
+#define mlx5_esw_for_all_vports(esw, i, vport)                 \
+       for ((i) = MLX5_VPORT_PF;                               \
+            (vport) = &(esw)->vports[i],                       \
+            (i) < (esw)->total_vports; (i)++)
+
+#define mlx5_esw_for_each_vf_vport(esw, i, vport, nvfs)        \
+       for ((i) = MLX5_VPORT_FIRST_VF;                         \
+            (vport) = &(esw)->vports[i],                       \
+            (i) <= (nvfs); (i)++)
+
+static struct mlx5_vport *mlx5_eswitch_get_vport(struct mlx5_eswitch *esw,
+                                                u16 vport_num)
+{
+       u16 idx = mlx5_eswitch_vport_num_to_index(esw, vport_num);
+
+       WARN_ON(vport_num > esw->total_vports - 1);
+       return &esw->vports[idx];
+}
+
 static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport,
                                        u32 events_mask)
 {
@@ -115,7 +136,7 @@ static int modify_esw_vport_context_cmd(struct mlx5_core_dev *dev, u16 vport,
        return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
 }
 
-static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
+static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u16 vport,
                                  u16 vlan, u8 qos, u8 set_flags)
 {
        u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {0};
@@ -152,7 +173,7 @@ static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
 
 /* E-Switch FDB */
 static struct mlx5_flow_handle *
-__esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule,
+__esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u16 vport, bool rx_rule,
                         u8 mac_c[ETH_ALEN], u8 mac_v[ETH_ALEN])
 {
        int match_header = (is_zero_ether_addr(mac_c) ? 0 :
@@ -188,7 +209,7 @@ __esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule,
                                        misc_parameters);
                mc_misc  = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
                                        misc_parameters);
-               MLX5_SET(fte_match_set_misc, mv_misc, source_port, UPLINK_VPORT);
+               MLX5_SET(fte_match_set_misc, mv_misc, source_port, MLX5_VPORT_UPLINK);
                MLX5_SET_TO_ONES(fte_match_set_misc, mc_misc, source_port);
        }
 
@@ -215,7 +236,7 @@ __esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule,
 }
 
 static struct mlx5_flow_handle *
-esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport)
+esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u16 vport)
 {
        u8 mac_c[ETH_ALEN];
 
@@ -224,7 +245,7 @@ esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport)
 }
 
 static struct mlx5_flow_handle *
-esw_fdb_set_vport_allmulti_rule(struct mlx5_eswitch *esw, u32 vport)
+esw_fdb_set_vport_allmulti_rule(struct mlx5_eswitch *esw, u16 vport)
 {
        u8 mac_c[ETH_ALEN];
        u8 mac_v[ETH_ALEN];
@@ -237,7 +258,7 @@ esw_fdb_set_vport_allmulti_rule(struct mlx5_eswitch *esw, u32 vport)
 }
 
 static struct mlx5_flow_handle *
-esw_fdb_set_vport_promisc_rule(struct mlx5_eswitch *esw, u32 vport)
+esw_fdb_set_vport_promisc_rule(struct mlx5_eswitch *esw, u16 vport)
 {
        u8 mac_c[ETH_ALEN];
        u8 mac_v[ETH_ALEN];
@@ -377,19 +398,19 @@ typedef int (*vport_addr_action)(struct mlx5_eswitch *esw,
 static int esw_add_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
 {
        u8 *mac = vaddr->node.addr;
-       u32 vport = vaddr->vport;
+       u16 vport = vaddr->vport;
        int err;
 
-       /* Skip mlx5_mpfs_add_mac for PFs,
-        * it is already done by the PF netdev in mlx5e_execute_l2_action
+       /* Skip mlx5_mpfs_add_mac for eswitch_managers,
+        * it is already done by its netdev in mlx5e_execute_l2_action
         */
-       if (!vport)
+       if (esw->manager_vport == vport)
                goto fdb_add;
 
        err = mlx5_mpfs_add_mac(esw->dev, mac);
        if (err) {
                esw_warn(esw->dev,
-                        "Failed to add L2 table mac(%pM) for vport(%d), err(%d)\n",
+                        "Failed to add L2 table mac(%pM) for vport(0x%x), err(%d)\n",
                         mac, vport, err);
                return err;
        }
@@ -409,13 +430,13 @@ fdb_add:
 static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
 {
        u8 *mac = vaddr->node.addr;
-       u32 vport = vaddr->vport;
+       u16 vport = vaddr->vport;
        int err = 0;
 
-       /* Skip mlx5_mpfs_del_mac for PFs,
-        * it is already done by the PF netdev in mlx5e_execute_l2_action
+       /* Skip mlx5_mpfs_del_mac for eswitch managerss,
+        * it is already done by its netdev in mlx5e_execute_l2_action
         */
-       if (!vport || !vaddr->mpfs)
+       if (!vaddr->mpfs || esw->manager_vport == vport)
                goto fdb_del;
 
        err = mlx5_mpfs_del_mac(esw->dev, mac);
@@ -438,17 +459,18 @@ static void update_allmulti_vports(struct mlx5_eswitch *esw,
                                   struct esw_mc_addr *esw_mc)
 {
        u8 *mac = vaddr->node.addr;
-       u32 vport_idx = 0;
+       struct mlx5_vport *vport;
+       u16 i, vport_num;
 
-       for (vport_idx = 0; vport_idx < esw->total_vports; vport_idx++) {
-               struct mlx5_vport *vport = &esw->vports[vport_idx];
+       mlx5_esw_for_all_vports(esw, i, vport) {
                struct hlist_head *vport_hash = vport->mc_list;
                struct vport_addr *iter_vaddr =
                                        l2addr_hash_find(vport_hash,
                                                         mac,
                                                         struct vport_addr);
+               vport_num = vport->vport;
                if (IS_ERR_OR_NULL(vport->allmulti_rule) ||
-                   vaddr->vport == vport_idx)
+                   vaddr->vport == vport_num)
                        continue;
                switch (vaddr->action) {
                case MLX5_ACTION_ADD:
@@ -460,14 +482,14 @@ static void update_allmulti_vports(struct mlx5_eswitch *esw,
                        if (!iter_vaddr) {
                                esw_warn(esw->dev,
                                         "ALL-MULTI: Failed to add MAC(%pM) to vport[%d] DB\n",
-                                        mac, vport_idx);
+                                        mac, vport_num);
                                continue;
                        }
-                       iter_vaddr->vport = vport_idx;
+                       iter_vaddr->vport = vport_num;
                        iter_vaddr->flow_rule =
                                        esw_fdb_set_vport_rule(esw,
                                                               mac,
-                                                              vport_idx);
+                                                              vport_num);
                        iter_vaddr->mc_promisc = true;
                        break;
                case MLX5_ACTION_DEL:
@@ -485,7 +507,7 @@ static int esw_add_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
        struct hlist_head *hash = esw->mc_table;
        struct esw_mc_addr *esw_mc;
        u8 *mac = vaddr->node.addr;
-       u32 vport = vaddr->vport;
+       u16 vport = vaddr->vport;
 
        if (!esw->fdb_table.legacy.fdb)
                return 0;
@@ -499,7 +521,7 @@ static int esw_add_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
                return -ENOMEM;
 
        esw_mc->uplink_rule = /* Forward MC MAC to Uplink */
-               esw_fdb_set_vport_rule(esw, mac, UPLINK_VPORT);
+               esw_fdb_set_vport_rule(esw, mac, MLX5_VPORT_UPLINK);
 
        /* Add this multicast mac to all the mc promiscuous vports */
        update_allmulti_vports(esw, vaddr, esw_mc);
@@ -525,7 +547,7 @@ static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
        struct hlist_head *hash = esw->mc_table;
        struct esw_mc_addr *esw_mc;
        u8 *mac = vaddr->node.addr;
-       u32 vport = vaddr->vport;
+       u16 vport = vaddr->vport;
 
        if (!esw->fdb_table.legacy.fdb)
                return 0;
@@ -564,9 +586,9 @@ static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
 
 /* Apply vport UC/MC list to HW l2 table and FDB table */
 static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw,
-                                     u32 vport_num, int list_type)
+                                     u16 vport_num, int list_type)
 {
-       struct mlx5_vport *vport = &esw->vports[vport_num];
+       struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
        bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
        vport_addr_action vport_addr_add;
        vport_addr_action vport_addr_del;
@@ -599,9 +621,9 @@ static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw,
 
 /* Sync vport UC/MC list from vport context */
 static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
-                                      u32 vport_num, int list_type)
+                                      u16 vport_num, int list_type)
 {
-       struct mlx5_vport *vport = &esw->vports[vport_num];
+       struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
        bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
        u8 (*mac_list)[ETH_ALEN];
        struct l2addr_node *node;
@@ -686,9 +708,9 @@ out:
 /* Sync vport UC/MC list from vport context
  * Must be called after esw_update_vport_addr_list
  */
-static void esw_update_vport_mc_promisc(struct mlx5_eswitch *esw, u32 vport_num)
+static void esw_update_vport_mc_promisc(struct mlx5_eswitch *esw, u16 vport_num)
 {
-       struct mlx5_vport *vport = &esw->vports[vport_num];
+       struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
        struct l2addr_node *node;
        struct vport_addr *addr;
        struct hlist_head *hash;
@@ -721,11 +743,11 @@ static void esw_update_vport_mc_promisc(struct mlx5_eswitch *esw, u32 vport_num)
 }
 
 /* Apply vport rx mode to HW FDB table */
-static void esw_apply_vport_rx_mode(struct mlx5_eswitch *esw, u32 vport_num,
+static void esw_apply_vport_rx_mode(struct mlx5_eswitch *esw, u16 vport_num,
                                    bool promisc, bool mc_promisc)
 {
+       struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
        struct esw_mc_addr *allmulti_addr = &esw->mc_promisc;
-       struct mlx5_vport *vport = &esw->vports[vport_num];
 
        if (IS_ERR_OR_NULL(vport->allmulti_rule) != mc_promisc)
                goto promisc;
@@ -736,7 +758,7 @@ static void esw_apply_vport_rx_mode(struct mlx5_eswitch *esw, u32 vport_num,
                if (!allmulti_addr->uplink_rule)
                        allmulti_addr->uplink_rule =
                                esw_fdb_set_vport_allmulti_rule(esw,
-                                                               UPLINK_VPORT);
+                                                               MLX5_VPORT_UPLINK);
                allmulti_addr->refcnt++;
        } else if (vport->allmulti_rule) {
                mlx5_del_flow_rules(vport->allmulti_rule);
@@ -764,9 +786,9 @@ promisc:
 }
 
 /* Sync vport rx mode from vport context */
-static void esw_update_vport_rx_mode(struct mlx5_eswitch *esw, u32 vport_num)
+static void esw_update_vport_rx_mode(struct mlx5_eswitch *esw, u16 vport_num)
 {
-       struct mlx5_vport *vport = &esw->vports[vport_num];
+       struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
        int promisc_all = 0;
        int promisc_uc = 0;
        int promisc_mc = 0;
@@ -1134,13 +1156,6 @@ static int esw_vport_ingress_config(struct mlx5_eswitch *esw,
        int err = 0;
        u8 *smac_v;
 
-       if (vport->info.spoofchk && !is_valid_ether_addr(vport->info.mac)) {
-               mlx5_core_warn(esw->dev,
-                              "vport[%d] configure ingress rules failed, illegal mac with spoofchk\n",
-                              vport->vport);
-               return -EPERM;
-       }
-
        esw_vport_cleanup_ingress_rules(esw, vport);
 
        if (!vport->info.vlan && !vport->info.qos && !vport->info.spoofchk) {
@@ -1350,8 +1365,8 @@ static void esw_destroy_tsar(struct mlx5_eswitch *esw)
 static int esw_vport_enable_qos(struct mlx5_eswitch *esw, int vport_num,
                                u32 initial_max_rate, u32 initial_bw_share)
 {
+       struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
        u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0};
-       struct mlx5_vport *vport = &esw->vports[vport_num];
        struct mlx5_core_dev *dev = esw->dev;
        void *vport_elem;
        int err = 0;
@@ -1390,7 +1405,7 @@ static int esw_vport_enable_qos(struct mlx5_eswitch *esw, int vport_num,
 
 static void esw_vport_disable_qos(struct mlx5_eswitch *esw, int vport_num)
 {
-       struct mlx5_vport *vport = &esw->vports[vport_num];
+       struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
        int err = 0;
 
        if (!vport->qos.enabled)
@@ -1409,8 +1424,8 @@ static void esw_vport_disable_qos(struct mlx5_eswitch *esw, int vport_num)
 static int esw_vport_qos_config(struct mlx5_eswitch *esw, int vport_num,
                                u32 max_rate, u32 bw_share)
 {
+       struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
        u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0};
-       struct mlx5_vport *vport = &esw->vports[vport_num];
        struct mlx5_core_dev *dev = esw->dev;
        void *vport_elem;
        u32 bitmask = 0;
@@ -1466,15 +1481,22 @@ static void esw_apply_vport_conf(struct mlx5_eswitch *esw,
 {
        int vport_num = vport->vport;
 
-       if (!vport_num)
+       if (esw->manager_vport == vport_num)
                return;
 
        mlx5_modify_vport_admin_state(esw->dev,
                                      MLX5_VPORT_STATE_OP_MOD_ESW_VPORT,
-                                     vport_num,
+                                     vport_num, 1,
                                      vport->info.link_state);
-       mlx5_modify_nic_vport_mac_address(esw->dev, vport_num, vport->info.mac);
-       mlx5_modify_nic_vport_node_guid(esw->dev, vport_num, vport->info.node_guid);
+
+       /* Host PF has its own mac/guid. */
+       if (vport_num) {
+               mlx5_modify_nic_vport_mac_address(esw->dev, vport_num,
+                                                 vport->info.mac);
+               mlx5_modify_nic_vport_node_guid(esw->dev, vport_num,
+                                               vport->info.node_guid);
+       }
+
        modify_esw_vport_cvlan(esw->dev, vport_num, vport->info.vlan, vport->info.qos,
                               (vport->info.vlan || vport->info.qos));
 
@@ -1520,10 +1542,10 @@ static void esw_vport_destroy_drop_counters(struct mlx5_vport *vport)
                mlx5_fc_destroy(dev, vport->egress.drop_counter);
 }
 
-static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num,
+static void esw_enable_vport(struct mlx5_eswitch *esw, struct mlx5_vport *vport,
                             int enable_events)
 {
-       struct mlx5_vport *vport = &esw->vports[vport_num];
+       u16 vport_num = vport->vport;
 
        mutex_lock(&esw->state_lock);
        WARN_ON(vport->enabled);
@@ -1546,8 +1568,11 @@ static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num,
        vport->enabled_events = enable_events;
        vport->enabled = true;
 
-       /* only PF is trusted by default */
-       if (!vport_num)
+       /* Esw manager is trusted by default. Host PF (vport 0) is trusted as well
+        * in smartNIC as it's a vport group manager.
+        */
+       if (esw->manager_vport == vport_num ||
+           (!vport_num && mlx5_core_is_ecpf(esw->dev)))
                vport->info.trusted = true;
 
        esw_vport_change_handle_locked(vport);
@@ -1557,9 +1582,10 @@ static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num,
        mutex_unlock(&esw->state_lock);
 }
 
-static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num)
+static void esw_disable_vport(struct mlx5_eswitch *esw,
+                             struct mlx5_vport *vport)
 {
-       struct mlx5_vport *vport = &esw->vports[vport_num];
+       u16 vport_num = vport->vport;
 
        if (!vport->enabled)
                return;
@@ -1580,10 +1606,10 @@ static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num)
        esw_vport_change_handle_locked(vport);
        vport->enabled_events = 0;
        esw_vport_disable_qos(esw, vport_num);
-       if (vport_num && esw->mode == SRIOV_LEGACY) {
+       if (esw->mode == SRIOV_LEGACY) {
                mlx5_modify_vport_admin_state(esw->dev,
                                              MLX5_VPORT_STATE_OP_MOD_ESW_VPORT,
-                                             vport_num,
+                                             vport_num, 1,
                                              MLX5_VPORT_ADMIN_STATE_DOWN);
                esw_vport_disable_egress_acl(esw, vport);
                esw_vport_disable_ingress_acl(esw, vport);
@@ -1602,7 +1628,7 @@ static int eswitch_vport_event(struct notifier_block *nb,
        u16 vport_num;
 
        vport_num = be16_to_cpu(eqe->data.vport_change.vport_num);
-       vport = &esw->vports[vport_num];
+       vport = mlx5_eswitch_get_vport(esw, vport_num);
        if (vport->enabled)
                queue_work(esw->work_queue, &vport->vport_change_handler);
 
@@ -1614,6 +1640,8 @@ static int eswitch_vport_event(struct notifier_block *nb,
 
 int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode)
 {
+       int vf_nvports = 0, total_nvports = 0;
+       struct mlx5_vport *vport;
        int err;
        int i, enabled_events;
 
@@ -1631,6 +1659,18 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode)
 
        esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d) mode (%d)\n", nvfs, mode);
 
+       if (mode == SRIOV_OFFLOADS) {
+               if (mlx5_core_is_ecpf_esw_manager(esw->dev)) {
+                       err = mlx5_query_host_params_num_vfs(esw->dev, &vf_nvports);
+                       if (err)
+                               return err;
+                       total_nvports = esw->total_vports;
+               } else {
+                       vf_nvports = nvfs;
+                       total_nvports = nvfs + MLX5_SPECIAL_VPORTS(esw->dev);
+               }
+       }
+
        esw->mode = mode;
 
        mlx5_lag_update(esw->dev);
@@ -1640,7 +1680,7 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode)
        } else {
                mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_ETH);
                mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
-               err = esw_offloads_init(esw, nvfs + 1);
+               err = esw_offloads_init(esw, vf_nvports, total_nvports);
        }
 
        if (err)
@@ -1655,8 +1695,20 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode)
         * 2. FDB/Eswitch is programmed by user space tools
         */
        enabled_events = (mode == SRIOV_LEGACY) ? SRIOV_VPORT_EVENTS : 0;
-       for (i = 0; i <= nvfs; i++)
-               esw_enable_vport(esw, i, enabled_events);
+
+       /* Enable PF vport */
+       vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_PF);
+       esw_enable_vport(esw, vport, enabled_events);
+
+       /* Enable ECPF vports */
+       if (mlx5_ecpf_vport_exists(esw->dev)) {
+               vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_ECPF);
+               esw_enable_vport(esw, vport, enabled_events);
+       }
+
+       /* Enable VF vports */
+       mlx5_esw_for_each_vf_vport(esw, i, vport, nvfs)
+               esw_enable_vport(esw, vport, enabled_events);
 
        if (mode == SRIOV_LEGACY) {
                MLX5_NB_INIT(&esw->nb, eswitch_vport_event, NIC_VPORT_CHANGE);
@@ -1681,8 +1733,8 @@ abort:
 void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
 {
        struct esw_mc_addr *mc_promisc;
+       struct mlx5_vport *vport;
        int old_mode;
-       int nvports;
        int i;
 
        if (!ESW_ALLOWED(esw) || esw->mode == SRIOV_NONE)
@@ -1692,13 +1744,12 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
                 esw->enabled_vports, esw->mode);
 
        mc_promisc = &esw->mc_promisc;
-       nvports = esw->enabled_vports;
 
        if (esw->mode == SRIOV_LEGACY)
                mlx5_eq_notifier_unregister(esw->dev, &esw->nb);
 
-       for (i = 0; i < esw->total_vports; i++)
-               esw_disable_vport(esw, i);
+       mlx5_esw_for_all_vports(esw, i, vport)
+               esw_disable_vport(esw, vport);
 
        if (mc_promisc && mc_promisc->uplink_rule)
                mlx5_del_flow_rules(mc_promisc->uplink_rule);
@@ -1708,7 +1759,7 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
        if (esw->mode == SRIOV_LEGACY)
                esw_destroy_legacy_fdb_table(esw);
        else if (esw->mode == SRIOV_OFFLOADS)
-               esw_offloads_cleanup(esw, nvports);
+               esw_offloads_cleanup(esw);
 
        old_mode = esw->mode;
        esw->mode = SRIOV_NONE;
@@ -1725,10 +1776,10 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev)
 {
        int total_vports = MLX5_TOTAL_VPORTS(dev);
        struct mlx5_eswitch *esw;
-       int vport_num;
-       int err;
+       struct mlx5_vport *vport;
+       int err, i;
 
-       if (!MLX5_ESWITCH_MANAGER(dev))
+       if (!MLX5_VPORT_MANAGER(dev))
                return 0;
 
        esw_info(dev,
@@ -1742,6 +1793,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev)
                return -ENOMEM;
 
        esw->dev = dev;
+       esw->manager_vport = mlx5_eswitch_manager_vport(dev);
 
        esw->work_queue = create_singlethread_workqueue("mlx5_esw_wq");
        if (!esw->work_queue) {
@@ -1756,6 +1808,8 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev)
                goto abort;
        }
 
+       esw->total_vports = total_vports;
+
        err = esw_offloads_init_reps(esw);
        if (err)
                goto abort;
@@ -1764,17 +1818,14 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev)
        hash_init(esw->offloads.mod_hdr_tbl);
        mutex_init(&esw->state_lock);
 
-       for (vport_num = 0; vport_num < total_vports; vport_num++) {
-               struct mlx5_vport *vport = &esw->vports[vport_num];
-
-               vport->vport = vport_num;
+       mlx5_esw_for_all_vports(esw, i, vport) {
+               vport->vport = mlx5_eswitch_index_to_vport_num(esw, i);
                vport->info.link_state = MLX5_VPORT_ADMIN_STATE_AUTO;
                vport->dev = dev;
                INIT_WORK(&vport->vport_change_handler,
                          esw_vport_change_handler);
        }
 
-       esw->total_vports = total_vports;
        esw->enabled_vports = 0;
        esw->mode = SRIOV_NONE;
        esw->offloads.inline_mode = MLX5_INLINE_MODE_NONE;
@@ -1797,7 +1848,7 @@ abort:
 
 void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw)
 {
-       if (!esw || !MLX5_ESWITCH_MANAGER(esw->dev))
+       if (!esw || !MLX5_VPORT_MANAGER(esw->dev))
                return;
 
        esw_info(esw->dev, "cleanup\n");
@@ -1827,13 +1878,10 @@ int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
        mutex_lock(&esw->state_lock);
        evport = &esw->vports[vport];
 
-       if (evport->info.spoofchk && !is_valid_ether_addr(mac)) {
+       if (evport->info.spoofchk && !is_valid_ether_addr(mac))
                mlx5_core_warn(esw->dev,
-                              "MAC invalidation is not allowed when spoofchk is on, vport(%d)\n",
+                              "Set invalid MAC while spoofchk is on, vport(%d)\n",
                               vport);
-               err = -EPERM;
-               goto unlock;
-       }
 
        err = mlx5_modify_nic_vport_mac_address(esw->dev, vport, mac);
        if (err) {
@@ -1876,7 +1924,7 @@ int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
 
        err = mlx5_modify_vport_admin_state(esw->dev,
                                            MLX5_VPORT_STATE_OP_MOD_ESW_VPORT,
-                                           vport, link_state);
+                                           vport, 1, link_state);
        if (err) {
                mlx5_core_warn(esw->dev,
                               "Failed to set vport %d link state, err = %d",
@@ -1979,6 +2027,10 @@ int mlx5_eswitch_set_vport_spoofchk(struct mlx5_eswitch *esw,
        evport = &esw->vports[vport];
        pschk = evport->info.spoofchk;
        evport->info.spoofchk = spoofchk;
+       if (pschk && !is_valid_ether_addr(evport->info.mac))
+               mlx5_core_warn(esw->dev,
+                              "Spoofchk in set while MAC is invalid, vport(%d)\n",
+                              evport->vport);
        if (evport->enabled && esw->mode == SRIOV_LEGACY)
                err = esw_vport_ingress_config(esw, evport);
        if (err)
@@ -2015,8 +2067,7 @@ static u32 calculate_vports_min_rate_divider(struct mlx5_eswitch *esw)
        u32 max_guarantee = 0;
        int i;
 
-       for (i = 0; i < esw->total_vports; i++) {
-               evport = &esw->vports[i];
+       mlx5_esw_for_all_vports(esw, i, evport) {
                if (!evport->enabled || evport->info.min_rate < max_guarantee)
                        continue;
                max_guarantee = evport->info.min_rate;
@@ -2035,8 +2086,7 @@ static int normalize_vports_min_rate(struct mlx5_eswitch *esw, u32 divider)
        int err;
        int i;
 
-       for (i = 0; i < esw->total_vports; i++) {
-               evport = &esw->vports[i];
+       mlx5_esw_for_all_vports(esw, i, evport) {
                if (!evport->enabled)
                        continue;
                vport_min_rate = evport->info.min_rate;
@@ -2051,7 +2101,7 @@ static int normalize_vports_min_rate(struct mlx5_eswitch *esw, u32 divider)
                if (bw_share == evport->qos.bw_share)
                        continue;
 
-               err = esw_vport_qos_config(esw, i, vport_max_rate,
+               err = esw_vport_qos_config(esw, evport->vport, vport_max_rate,
                                           bw_share);
                if (!err)
                        evport->qos.bw_share = bw_share;
@@ -2134,7 +2184,7 @@ static int mlx5_eswitch_query_vport_drop_stats(struct mlx5_core_dev *dev,
            !MLX5_CAP_GEN(dev, transmit_discard_vport_down))
                return 0;
 
-       err = mlx5_query_vport_down_stats(dev, vport_idx,
+       err = mlx5_query_vport_down_stats(dev, vport_idx, 1,
                                          &rx_discard_vport_down,
                                          &tx_discard_vport_down);
        if (err)
@@ -2171,8 +2221,7 @@ int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
                 MLX5_CMD_OP_QUERY_VPORT_COUNTER);
        MLX5_SET(query_vport_counter_in, in, op_mod, 0);
        MLX5_SET(query_vport_counter_in, in, vport_number, vport);
-       if (vport)
-               MLX5_SET(query_vport_counter_in, in, other_vport, 1);
+       MLX5_SET(query_vport_counter_in, in, other_vport, 1);
 
        memset(out, 0, outlen);
        err = mlx5_cmd_exec(esw->dev, in, sizeof(in), out, outlen);
index 9c89eea..af5581a 100644 (file)
@@ -38,6 +38,7 @@
 #include <net/devlink.h>
 #include <linux/mlx5/device.h>
 #include <linux/mlx5/eswitch.h>
+#include <linux/mlx5/vport.h>
 #include <linux/mlx5/fs.h>
 #include "lib/mpfs.h"
 
@@ -49,8 +50,6 @@
 #define MLX5_MAX_MC_PER_VPORT(dev) \
        (1 << MLX5_CAP_GEN(dev, log_max_current_mc_list))
 
-#define FDB_UPLINK_VPORT 0xffff
-
 #define MLX5_MIN_BW_SHARE 1
 
 #define MLX5_RATE_TO_BW_SHARE(rate, divider, limit) \
@@ -183,6 +182,16 @@ struct esw_mc_addr { /* SRIOV only */
        u32                    refcnt;
 };
 
+struct mlx5_host_work {
+       struct work_struct      work;
+       struct mlx5_eswitch     *esw;
+};
+
+struct mlx5_host_info {
+       struct mlx5_nb          nb;
+       u16                     num_vfs;
+};
+
 struct mlx5_eswitch {
        struct mlx5_core_dev    *dev;
        struct mlx5_nb          nb;
@@ -206,10 +215,13 @@ struct mlx5_eswitch {
        struct mlx5_esw_offload offloads;
        int                     mode;
        int                     nvports;
+       u16                     manager_vport;
+       struct mlx5_host_info   host_info;
 };
 
-void esw_offloads_cleanup(struct mlx5_eswitch *esw, int nvports);
-int esw_offloads_init(struct mlx5_eswitch *esw, int nvports);
+void esw_offloads_cleanup(struct mlx5_eswitch *esw);
+int esw_offloads_init(struct mlx5_eswitch *esw, int vf_nvports,
+                     int total_nvports);
 void esw_offloads_cleanup_reps(struct mlx5_eswitch *esw);
 int esw_offloads_init_reps(struct mlx5_eswitch *esw);
 
@@ -312,6 +324,7 @@ struct mlx5_esw_flow_attr {
        } dests[MLX5_MAX_FLOW_FWD_VPORTS];
        u32     mod_hdr_id;
        u8      match_level;
+       u8      tunnel_match_level;
        struct mlx5_fc *counter;
        u32     chain;
        u16     prio;
@@ -364,6 +377,53 @@ bool mlx5_esw_lag_prereq(struct mlx5_core_dev *dev0,
 
 #define esw_debug(dev, format, ...)                            \
        mlx5_core_dbg_mask(dev, MLX5_DEBUG_ESWITCH_MASK, format, ##__VA_ARGS__)
+
+/* The returned number is valid only when the dev is eswitch manager. */
+static inline u16 mlx5_eswitch_manager_vport(struct mlx5_core_dev *dev)
+{
+       return mlx5_core_is_ecpf_esw_manager(dev) ?
+               MLX5_VPORT_ECPF : MLX5_VPORT_PF;
+}
+
+static inline int mlx5_eswitch_uplink_idx(struct mlx5_eswitch *esw)
+{
+       /* Uplink always locate at the last element of the array.*/
+       return esw->total_vports - 1;
+}
+
+static inline int mlx5_eswitch_ecpf_idx(struct mlx5_eswitch *esw)
+{
+       return esw->total_vports - 2;
+}
+
+static inline int mlx5_eswitch_vport_num_to_index(struct mlx5_eswitch *esw,
+                                                 u16 vport_num)
+{
+       if (vport_num == MLX5_VPORT_ECPF) {
+               if (!mlx5_ecpf_vport_exists(esw->dev))
+                       esw_warn(esw->dev, "ECPF vport doesn't exist!\n");
+               return mlx5_eswitch_ecpf_idx(esw);
+       }
+
+       if (vport_num == MLX5_VPORT_UPLINK)
+               return mlx5_eswitch_uplink_idx(esw);
+
+       return vport_num;
+}
+
+static inline int mlx5_eswitch_index_to_vport_num(struct mlx5_eswitch *esw,
+                                                 int index)
+{
+       if (index == mlx5_eswitch_ecpf_idx(esw) &&
+           mlx5_ecpf_vport_exists(esw->dev))
+               return MLX5_VPORT_ECPF;
+
+       if (index == mlx5_eswitch_uplink_idx(esw))
+               return MLX5_VPORT_UPLINK;
+
+       return index;
+}
+
 #else  /* CONFIG_MLX5_ESWITCH */
 /* eswitch API stubs */
 static inline int  mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; }
index 53065b6..f226039 100644 (file)
 #include "en.h"
 #include "fs_core.h"
 #include "lib/devcom.h"
+#include "ecpf.h"
+#include "lib/eq.h"
 
 enum {
        FDB_FAST_PATH = 0,
        FDB_SLOW_PATH
 };
 
+/* There are two match-all miss flows, one for unicast dst mac and
+ * one for multicast.
+ */
+#define MLX5_ESW_MISS_FLOWS (2)
+
 #define fdb_prio_table(esw, chain, prio, level) \
        (esw)->fdb_table.offloads.fdb_prio[(chain)][(prio)][(level)]
 
+#define UPLINK_REP_INDEX 0
+
+/* The rep getter/iterator are only valid after esw->total_vports
+ * and vport->vport are initialized in mlx5_eswitch_init.
+ */
+#define mlx5_esw_for_all_reps(esw, i, rep)                     \
+       for ((i) = MLX5_VPORT_PF;                               \
+            (rep) = &(esw)->offloads.vport_reps[i],            \
+            (i) < (esw)->total_vports; (i)++)
+
+#define mlx5_esw_for_each_vf_rep(esw, i, rep, nvfs)            \
+       for ((i) = MLX5_VPORT_FIRST_VF;                         \
+            (rep) = &(esw)->offloads.vport_reps[i],            \
+            (i) <= (nvfs); (i)++)
+
+#define mlx5_esw_for_each_vf_rep_reverse(esw, i, rep, nvfs)    \
+       for ((i) = (nvfs);                                      \
+            (rep) = &(esw)->offloads.vport_reps[i],            \
+            (i) >= MLX5_VPORT_FIRST_VF; (i)--)
+
+#define mlx5_esw_for_each_vf_vport(esw, vport, nvfs)           \
+       for ((vport) = MLX5_VPORT_FIRST_VF;                     \
+            (vport) <= (nvfs); (vport)++)
+
+#define mlx5_esw_for_each_vf_vport_reverse(esw, vport, nvfs)   \
+       for ((vport) = (nvfs);                                  \
+            (vport) >= MLX5_VPORT_FIRST_VF; (vport)--)
+
+static struct mlx5_eswitch_rep *mlx5_eswitch_get_rep(struct mlx5_eswitch *esw,
+                                                    u16 vport_num)
+{
+       u16 idx = mlx5_eswitch_vport_num_to_index(esw, vport_num);
+
+       WARN_ON(idx > esw->total_vports - 1);
+       return &esw->offloads.vport_reps[idx];
+}
+
 static struct mlx5_flow_table *
 esw_get_prio_table(struct mlx5_eswitch *esw, u32 chain, u16 prio, int level);
 static void
@@ -160,14 +204,15 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
                MLX5_SET_TO_ONES(fte_match_set_misc, misc,
                                 source_eswitch_owner_vhca_id);
 
-       if (attr->match_level == MLX5_MATCH_NONE)
-               spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
-       else
-               spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS |
-                                             MLX5_MATCH_MISC_PARAMETERS;
-
-       if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DECAP)
-               spec->match_criteria_enable |= MLX5_MATCH_INNER_HEADERS;
+       spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
+       if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DECAP) {
+               if (attr->tunnel_match_level != MLX5_MATCH_NONE)
+                       spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
+               if (attr->match_level != MLX5_MATCH_NONE)
+                       spec->match_criteria_enable |= MLX5_MATCH_INNER_HEADERS;
+       } else if (attr->match_level != MLX5_MATCH_NONE) {
+               spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
+       }
 
        if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
                flow_act.modify_id = attr->mod_hdr_id;
@@ -318,7 +363,7 @@ static int esw_set_global_vlan_pop(struct mlx5_eswitch *esw, u8 val)
        esw_debug(esw->dev, "%s applying global %s policy\n", __func__, val ? "pop" : "none");
        for (vf_vport = 1; vf_vport < esw->enabled_vports; vf_vport++) {
                rep = &esw->offloads.vport_reps[vf_vport];
-               if (!rep->rep_if[REP_ETH].valid)
+               if (rep->rep_if[REP_ETH].state != REP_LOADED)
                        continue;
 
                err = __mlx5_eswitch_set_vport_vlan(esw, rep->vport, 0, 0, val);
@@ -359,15 +404,15 @@ static int esw_add_vlan_action_check(struct mlx5_esw_flow_attr *attr,
        in_rep  = attr->in_rep;
        out_rep = attr->dests[0].rep;
 
-       if (push && in_rep->vport == FDB_UPLINK_VPORT)
+       if (push && in_rep->vport == MLX5_VPORT_UPLINK)
                goto out_notsupp;
 
-       if (pop && out_rep->vport == FDB_UPLINK_VPORT)
+       if (pop && out_rep->vport == MLX5_VPORT_UPLINK)
                goto out_notsupp;
 
        /* vport has vlan push configured, can't offload VF --> wire rules w.o it */
        if (!push && !pop && fwd)
-               if (in_rep->vlan && out_rep->vport == FDB_UPLINK_VPORT)
+               if (in_rep->vlan && out_rep->vport == MLX5_VPORT_UPLINK)
                        goto out_notsupp;
 
        /* protects against (1) setting rules with different vlans to push and
@@ -409,7 +454,7 @@ int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw,
 
        if (!push && !pop && fwd) {
                /* tracks VF --> wire rules without vlan push action */
-               if (attr->dests[0].rep->vport == FDB_UPLINK_VPORT) {
+               if (attr->dests[0].rep->vport == MLX5_VPORT_UPLINK) {
                        vport->vlan_refcount++;
                        attr->vlan_handled = true;
                }
@@ -469,7 +514,7 @@ int mlx5_eswitch_del_vlan_action(struct mlx5_eswitch *esw,
 
        if (!push && !pop && fwd) {
                /* tracks VF --> wire rules without vlan push action */
-               if (attr->dests[0].rep->vport == FDB_UPLINK_VPORT)
+               if (attr->dests[0].rep->vport == MLX5_VPORT_UPLINK)
                        vport->vlan_refcount--;
 
                return 0;
@@ -516,7 +561,8 @@ mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, int vport, u32 sqn
 
        misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
        MLX5_SET(fte_match_set_misc, misc, source_sqn, sqn);
-       MLX5_SET(fte_match_set_misc, misc, source_port, 0x0); /* source vport is 0 */
+       /* source vport is the esw manager */
+       MLX5_SET(fte_match_set_misc, misc, source_port, esw->manager_vport);
 
        misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
        MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_sqn);
@@ -561,7 +607,7 @@ static void peer_miss_rules_setup(struct mlx5_core_dev *peer_dev,
                         source_eswitch_owner_vhca_id);
 
        dest->type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
-       dest->vport.num = 0;
+       dest->vport.num = peer_dev->priv.eswitch->manager_vport;
        dest->vport.vhca_id = MLX5_CAP_GEN(peer_dev, vhca_id);
        dest->vport.flags |= MLX5_FLOW_DEST_VPORT_VHCA_ID;
 }
@@ -595,14 +641,35 @@ static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw,
        misc = MLX5_ADDR_OF(fte_match_param, spec->match_value,
                            misc_parameters);
 
-       for (i = 1; i < nvports; i++) {
+       if (mlx5_core_is_ecpf_esw_manager(esw->dev)) {
+               MLX5_SET(fte_match_set_misc, misc, source_port, MLX5_VPORT_PF);
+               flow = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb,
+                                          spec, &flow_act, &dest, 1);
+               if (IS_ERR(flow)) {
+                       err = PTR_ERR(flow);
+                       goto add_pf_flow_err;
+               }
+               flows[MLX5_VPORT_PF] = flow;
+       }
+
+       if (mlx5_ecpf_vport_exists(esw->dev)) {
+               MLX5_SET(fte_match_set_misc, misc, source_port, MLX5_VPORT_ECPF);
+               flow = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb,
+                                          spec, &flow_act, &dest, 1);
+               if (IS_ERR(flow)) {
+                       err = PTR_ERR(flow);
+                       goto add_ecpf_flow_err;
+               }
+               flows[mlx5_eswitch_ecpf_idx(esw)] = flow;
+       }
+
+       mlx5_esw_for_each_vf_vport(esw, i, mlx5_core_max_vfs(esw->dev)) {
                MLX5_SET(fte_match_set_misc, misc, source_port, i);
                flow = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb,
                                           spec, &flow_act, &dest, 1);
                if (IS_ERR(flow)) {
                        err = PTR_ERR(flow);
-                       esw_warn(esw->dev, "FDB: Failed to add peer miss flow rule err %d\n", err);
-                       goto add_flow_err;
+                       goto add_vf_flow_err;
                }
                flows[i] = flow;
        }
@@ -612,9 +679,18 @@ static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw,
        kvfree(spec);
        return 0;
 
-add_flow_err:
-       for (i--; i > 0; i--)
+add_vf_flow_err:
+       nvports = --i;
+       mlx5_esw_for_each_vf_vport_reverse(esw, i, nvports)
                mlx5_del_flow_rules(flows[i]);
+
+       if (mlx5_ecpf_vport_exists(esw->dev))
+               mlx5_del_flow_rules(flows[mlx5_eswitch_ecpf_idx(esw)]);
+add_ecpf_flow_err:
+       if (mlx5_core_is_ecpf_esw_manager(esw->dev))
+               mlx5_del_flow_rules(flows[MLX5_VPORT_PF]);
+add_pf_flow_err:
+       esw_warn(esw->dev, "FDB: Failed to add peer miss flow rule err %d\n", err);
        kvfree(flows);
 alloc_flows_err:
        kvfree(spec);
@@ -628,9 +704,15 @@ static void esw_del_fdb_peer_miss_rules(struct mlx5_eswitch *esw)
 
        flows = esw->fdb_table.offloads.peer_miss_rules;
 
-       for (i = 1; i < esw->total_vports; i++)
+       mlx5_esw_for_each_vf_vport_reverse(esw, i, mlx5_core_max_vfs(esw->dev))
                mlx5_del_flow_rules(flows[i]);
 
+       if (mlx5_ecpf_vport_exists(esw->dev))
+               mlx5_del_flow_rules(flows[mlx5_eswitch_ecpf_idx(esw)]);
+
+       if (mlx5_core_is_ecpf_esw_manager(esw->dev))
+               mlx5_del_flow_rules(flows[MLX5_VPORT_PF]);
+
        kvfree(flows);
 }
 
@@ -660,7 +742,7 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
        dmac_c[0] = 0x01;
 
        dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
-       dest.vport.num = 0;
+       dest.vport.num = esw->manager_vport;
        flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
 
        flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb, spec,
@@ -904,8 +986,8 @@ static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw, int nvports)
                esw->fdb_table.offloads.fdb_left[i] =
                        ESW_POOLS[i] <= fdb_max ? ESW_SIZE / ESW_POOLS[i] : 0;
 
-       table_size = nvports * MAX_SQ_NVPORTS + MAX_PF_SQ + 2 +
-               esw->total_vports;
+       table_size = nvports * MAX_SQ_NVPORTS + MAX_PF_SQ +
+               MLX5_ESW_MISS_FLOWS + esw->total_vports;
 
        /* create the slow path fdb with encap set, so further table instances
         * can be created at run time while VFs are probed if the FW allows that.
@@ -999,7 +1081,8 @@ static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw, int nvports)
        dmac[0] = 0x01;
 
        MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, ix);
-       MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ix + 2);
+       MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index,
+                ix + MLX5_ESW_MISS_FLOWS);
 
        g = mlx5_create_flow_group(fdb, flow_group_in);
        if (IS_ERR(g)) {
@@ -1048,7 +1131,7 @@ static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw)
        esw_destroy_offloads_fast_fdb_tables(esw);
 }
 
-static int esw_create_offloads_table(struct mlx5_eswitch *esw)
+static int esw_create_offloads_table(struct mlx5_eswitch *esw, int nvports)
 {
        struct mlx5_flow_table_attr ft_attr = {};
        struct mlx5_core_dev *dev = esw->dev;
@@ -1062,7 +1145,7 @@ static int esw_create_offloads_table(struct mlx5_eswitch *esw)
                return -EOPNOTSUPP;
        }
 
-       ft_attr.max_fte = dev->priv.sriov.num_vfs + 2;
+       ft_attr.max_fte = nvports + MLX5_ESW_MISS_FLOWS;
 
        ft_offloads = mlx5_create_flow_table(ns, &ft_attr);
        if (IS_ERR(ft_offloads)) {
@@ -1082,16 +1165,15 @@ static void esw_destroy_offloads_table(struct mlx5_eswitch *esw)
        mlx5_destroy_flow_table(offloads->ft_offloads);
 }
 
-static int esw_create_vport_rx_group(struct mlx5_eswitch *esw)
+static int esw_create_vport_rx_group(struct mlx5_eswitch *esw, int nvports)
 {
        int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
        struct mlx5_flow_group *g;
-       struct mlx5_priv *priv = &esw->dev->priv;
        u32 *flow_group_in;
        void *match_criteria, *misc;
        int err = 0;
-       int nvports = priv->sriov.num_vfs + 2;
 
+       nvports = nvports + MLX5_ESW_MISS_FLOWS;
        flow_group_in = kvzalloc(inlen, GFP_KERNEL);
        if (!flow_group_in)
                return -ENOMEM;
@@ -1168,7 +1250,8 @@ static int esw_offloads_start(struct mlx5_eswitch *esw,
 {
        int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs;
 
-       if (esw->mode != SRIOV_LEGACY) {
+       if (esw->mode != SRIOV_LEGACY &&
+           !mlx5_core_is_ecpf_esw_manager(esw->dev)) {
                NL_SET_ERR_MSG_MOD(extack,
                                   "Can't set offloads mode, SRIOV legacy not enabled");
                return -EINVAL;
@@ -1206,9 +1289,8 @@ int esw_offloads_init_reps(struct mlx5_eswitch *esw)
 {
        int total_vfs = MLX5_TOTAL_VPORTS(esw->dev);
        struct mlx5_core_dev *dev = esw->dev;
-       struct mlx5_esw_offload *offloads;
        struct mlx5_eswitch_rep *rep;
-       u8 hw_id[ETH_ALEN];
+       u8 hw_id[ETH_ALEN], rep_type;
        int vport;
 
        esw->offloads.vport_reps = kcalloc(total_vfs,
@@ -1217,75 +1299,203 @@ int esw_offloads_init_reps(struct mlx5_eswitch *esw)
        if (!esw->offloads.vport_reps)
                return -ENOMEM;
 
-       offloads = &esw->offloads;
        mlx5_query_nic_vport_mac_address(dev, 0, hw_id);
 
-       for (vport = 0; vport < total_vfs; vport++) {
-               rep = &offloads->vport_reps[vport];
-
-               rep->vport = vport;
+       mlx5_esw_for_all_reps(esw, vport, rep) {
+               rep->vport = mlx5_eswitch_index_to_vport_num(esw, vport);
                ether_addr_copy(rep->hw_id, hw_id);
-       }
 
-       offloads->vport_reps[0].vport = FDB_UPLINK_VPORT;
+               for (rep_type = 0; rep_type < NUM_REP_TYPES; rep_type++)
+                       rep->rep_if[rep_type].state = REP_UNREGISTERED;
+       }
 
        return 0;
 }
 
-static void esw_offloads_unload_reps_type(struct mlx5_eswitch *esw, int nvports,
-                                         u8 rep_type)
+static void __esw_offloads_unload_rep(struct mlx5_eswitch *esw,
+                                     struct mlx5_eswitch_rep *rep, u8 rep_type)
+{
+       if (rep->rep_if[rep_type].state != REP_LOADED)
+               return;
+
+       rep->rep_if[rep_type].unload(rep);
+       rep->rep_if[rep_type].state = REP_REGISTERED;
+}
+
+static void __unload_reps_special_vport(struct mlx5_eswitch *esw, u8 rep_type)
 {
        struct mlx5_eswitch_rep *rep;
-       int vport;
 
-       for (vport = nvports - 1; vport >= 0; vport--) {
-               rep = &esw->offloads.vport_reps[vport];
-               if (!rep->rep_if[rep_type].valid)
-                       continue;
+       if (mlx5_ecpf_vport_exists(esw->dev)) {
+               rep = mlx5_eswitch_get_rep(esw, MLX5_VPORT_ECPF);
+               __esw_offloads_unload_rep(esw, rep, rep_type);
+       }
 
-               rep->rep_if[rep_type].unload(rep);
+       if (mlx5_core_is_ecpf_esw_manager(esw->dev)) {
+               rep = mlx5_eswitch_get_rep(esw, MLX5_VPORT_PF);
+               __esw_offloads_unload_rep(esw, rep, rep_type);
        }
+
+       rep = mlx5_eswitch_get_rep(esw, MLX5_VPORT_UPLINK);
+       __esw_offloads_unload_rep(esw, rep, rep_type);
 }
 
-static void esw_offloads_unload_reps(struct mlx5_eswitch *esw, int nvports)
+static void __unload_reps_vf_vport(struct mlx5_eswitch *esw, int nvports,
+                                  u8 rep_type)
+{
+       struct mlx5_eswitch_rep *rep;
+       int i;
+
+       mlx5_esw_for_each_vf_rep_reverse(esw, i, rep, nvports)
+               __esw_offloads_unload_rep(esw, rep, rep_type);
+}
+
+static void esw_offloads_unload_vf_reps(struct mlx5_eswitch *esw, int nvports)
+{
+       u8 rep_type = NUM_REP_TYPES;
+
+       while (rep_type-- > 0)
+               __unload_reps_vf_vport(esw, nvports, rep_type);
+}
+
+static void __unload_reps_all_vport(struct mlx5_eswitch *esw, int nvports,
+                                   u8 rep_type)
+{
+       __unload_reps_vf_vport(esw, nvports, rep_type);
+
+       /* Special vports must be the last to unload. */
+       __unload_reps_special_vport(esw, rep_type);
+}
+
+static void esw_offloads_unload_all_reps(struct mlx5_eswitch *esw, int nvports)
 {
        u8 rep_type = NUM_REP_TYPES;
 
        while (rep_type-- > 0)
-               esw_offloads_unload_reps_type(esw, nvports, rep_type);
+               __unload_reps_all_vport(esw, nvports, rep_type);
+}
+
+static int __esw_offloads_load_rep(struct mlx5_eswitch *esw,
+                                  struct mlx5_eswitch_rep *rep, u8 rep_type)
+{
+       int err = 0;
+
+       if (rep->rep_if[rep_type].state != REP_REGISTERED)
+               return 0;
+
+       err = rep->rep_if[rep_type].load(esw->dev, rep);
+       if (err)
+               return err;
+
+       rep->rep_if[rep_type].state = REP_LOADED;
+
+       return 0;
 }
 
-static int esw_offloads_load_reps_type(struct mlx5_eswitch *esw, int nvports,
-                                      u8 rep_type)
+static int __load_reps_special_vport(struct mlx5_eswitch *esw, u8 rep_type)
 {
        struct mlx5_eswitch_rep *rep;
-       int vport;
        int err;
 
-       for (vport = 0; vport < nvports; vport++) {
-               rep = &esw->offloads.vport_reps[vport];
-               if (!rep->rep_if[rep_type].valid)
-                       continue;
+       rep = mlx5_eswitch_get_rep(esw, MLX5_VPORT_UPLINK);
+       err = __esw_offloads_load_rep(esw, rep, rep_type);
+       if (err)
+               return err;
 
-               err = rep->rep_if[rep_type].load(esw->dev, rep);
+       if (mlx5_core_is_ecpf_esw_manager(esw->dev)) {
+               rep = mlx5_eswitch_get_rep(esw, MLX5_VPORT_PF);
+               err = __esw_offloads_load_rep(esw, rep, rep_type);
                if (err)
-                       goto err_reps;
+                       goto err_pf;
+       }
+
+       if (mlx5_ecpf_vport_exists(esw->dev)) {
+               rep = mlx5_eswitch_get_rep(esw, MLX5_VPORT_ECPF);
+               err = __esw_offloads_load_rep(esw, rep, rep_type);
+               if (err)
+                       goto err_ecpf;
+       }
+
+       return 0;
+
+err_ecpf:
+       if (mlx5_core_is_ecpf_esw_manager(esw->dev)) {
+               rep = mlx5_eswitch_get_rep(esw, MLX5_VPORT_PF);
+               __esw_offloads_unload_rep(esw, rep, rep_type);
+       }
+
+err_pf:
+       rep = mlx5_eswitch_get_rep(esw, MLX5_VPORT_UPLINK);
+       __esw_offloads_unload_rep(esw, rep, rep_type);
+       return err;
+}
+
+static int __load_reps_vf_vport(struct mlx5_eswitch *esw, int nvports,
+                               u8 rep_type)
+{
+       struct mlx5_eswitch_rep *rep;
+       int err, i;
+
+       mlx5_esw_for_each_vf_rep(esw, i, rep, nvports) {
+               err = __esw_offloads_load_rep(esw, rep, rep_type);
+               if (err)
+                       goto err_vf;
        }
 
        return 0;
 
+err_vf:
+       __unload_reps_vf_vport(esw, --i, rep_type);
+       return err;
+}
+
+static int esw_offloads_load_vf_reps(struct mlx5_eswitch *esw, int nvports)
+{
+       u8 rep_type = 0;
+       int err;
+
+       for (rep_type = 0; rep_type < NUM_REP_TYPES; rep_type++) {
+               err = __load_reps_vf_vport(esw, nvports, rep_type);
+               if (err)
+                       goto err_reps;
+       }
+
+       return err;
+
 err_reps:
-       esw_offloads_unload_reps_type(esw, vport, rep_type);
+       while (rep_type-- > 0)
+               __unload_reps_vf_vport(esw, nvports, rep_type);
+       return err;
+}
+
+static int __load_reps_all_vport(struct mlx5_eswitch *esw, int nvports,
+                                u8 rep_type)
+{
+       int err;
+
+       /* Special vports must be loaded first. */
+       err = __load_reps_special_vport(esw, rep_type);
+       if (err)
+               return err;
+
+       err = __load_reps_vf_vport(esw, nvports, rep_type);
+       if (err)
+               goto err_vfs;
+
+       return 0;
+
+err_vfs:
+       __unload_reps_special_vport(esw, rep_type);
        return err;
 }
 
-static int esw_offloads_load_reps(struct mlx5_eswitch *esw, int nvports)
+static int esw_offloads_load_all_reps(struct mlx5_eswitch *esw, int nvports)
 {
        u8 rep_type = 0;
        int err;
 
        for (rep_type = 0; rep_type < NUM_REP_TYPES; rep_type++) {
-               err = esw_offloads_load_reps_type(esw, nvports, rep_type);
+               err = __load_reps_all_vport(esw, nvports, rep_type);
                if (err)
                        goto err_reps;
        }
@@ -1294,7 +1504,7 @@ static int esw_offloads_load_reps(struct mlx5_eswitch *esw, int nvports)
 
 err_reps:
        while (rep_type-- > 0)
-               esw_offloads_unload_reps_type(esw, nvports, rep_type);
+               __unload_reps_all_vport(esw, nvports, rep_type);
        return err;
 }
 
@@ -1397,7 +1607,7 @@ static void esw_offloads_devcom_cleanup(struct mlx5_eswitch *esw)
        mlx5_devcom_unregister_component(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
 }
 
-int esw_offloads_init(struct mlx5_eswitch *esw, int nvports)
+static int esw_offloads_steering_init(struct mlx5_eswitch *esw, int nvports)
 {
        int err;
 
@@ -1407,24 +1617,16 @@ int esw_offloads_init(struct mlx5_eswitch *esw, int nvports)
        if (err)
                return err;
 
-       err = esw_create_offloads_table(esw);
+       err = esw_create_offloads_table(esw, nvports);
        if (err)
                goto create_ft_err;
 
-       err = esw_create_vport_rx_group(esw);
+       err = esw_create_vport_rx_group(esw, nvports);
        if (err)
                goto create_fg_err;
 
-       err = esw_offloads_load_reps(esw, nvports);
-       if (err)
-               goto err_reps;
-
-       esw_offloads_devcom_init(esw);
        return 0;
 
-err_reps:
-       esw_destroy_vport_rx_group(esw);
-
 create_fg_err:
        esw_destroy_offloads_table(esw);
 
@@ -1434,6 +1636,95 @@ create_ft_err:
        return err;
 }
 
+static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw)
+{
+       esw_destroy_vport_rx_group(esw);
+       esw_destroy_offloads_table(esw);
+       esw_destroy_offloads_fdb_tables(esw);
+}
+
+static void esw_host_params_event_handler(struct work_struct *work)
+{
+       struct mlx5_host_work *host_work;
+       struct mlx5_eswitch *esw;
+       int err, num_vf = 0;
+
+       host_work = container_of(work, struct mlx5_host_work, work);
+       esw = host_work->esw;
+
+       err = mlx5_query_host_params_num_vfs(esw->dev, &num_vf);
+       if (err || num_vf == esw->host_info.num_vfs)
+               goto out;
+
+       /* Number of VFs can only change from "0 to x" or "x to 0". */
+       if (esw->host_info.num_vfs > 0) {
+               esw_offloads_unload_vf_reps(esw, esw->host_info.num_vfs);
+       } else {
+               err = esw_offloads_load_vf_reps(esw, num_vf);
+
+               if (err)
+                       goto out;
+       }
+
+       esw->host_info.num_vfs = num_vf;
+
+out:
+       kfree(host_work);
+}
+
+static int esw_host_params_event(struct notifier_block *nb,
+                                unsigned long type, void *data)
+{
+       struct mlx5_host_work *host_work;
+       struct mlx5_host_info *host_info;
+       struct mlx5_eswitch *esw;
+
+       host_work = kzalloc(sizeof(*host_work), GFP_ATOMIC);
+       if (!host_work)
+               return NOTIFY_DONE;
+
+       host_info = mlx5_nb_cof(nb, struct mlx5_host_info, nb);
+       esw = container_of(host_info, struct mlx5_eswitch, host_info);
+
+       host_work->esw = esw;
+
+       INIT_WORK(&host_work->work, esw_host_params_event_handler);
+       queue_work(esw->work_queue, &host_work->work);
+
+       return NOTIFY_OK;
+}
+
+int esw_offloads_init(struct mlx5_eswitch *esw, int vf_nvports,
+                     int total_nvports)
+{
+       int err;
+
+       mutex_init(&esw->fdb_table.offloads.fdb_prio_lock);
+
+       err = esw_offloads_steering_init(esw, total_nvports);
+       if (err)
+               return err;
+
+       err = esw_offloads_load_all_reps(esw, vf_nvports);
+       if (err)
+               goto err_reps;
+
+       esw_offloads_devcom_init(esw);
+
+       if (mlx5_core_is_ecpf_esw_manager(esw->dev)) {
+               MLX5_NB_INIT(&esw->host_info.nb, esw_host_params_event,
+                            HOST_PARAMS_CHANGE);
+               mlx5_eq_notifier_register(esw->dev, &esw->host_info.nb);
+               esw->host_info.num_vfs = vf_nvports;
+       }
+
+       return 0;
+
+err_reps:
+       esw_offloads_steering_cleanup(esw);
+       return err;
+}
+
 static int esw_offloads_stop(struct mlx5_eswitch *esw,
                             struct netlink_ext_ack *extack)
 {
@@ -1453,13 +1744,21 @@ static int esw_offloads_stop(struct mlx5_eswitch *esw,
        return err;
 }
 
-void esw_offloads_cleanup(struct mlx5_eswitch *esw, int nvports)
+void esw_offloads_cleanup(struct mlx5_eswitch *esw)
 {
+       u16 num_vfs;
+
+       if (mlx5_core_is_ecpf_esw_manager(esw->dev)) {
+               mlx5_eq_notifier_unregister(esw->dev, &esw->host_info.nb);
+               flush_workqueue(esw->work_queue);
+               num_vfs = esw->host_info.num_vfs;
+       } else {
+               num_vfs = esw->dev->priv.sriov.num_vfs;
+       }
+
        esw_offloads_devcom_cleanup(esw);
-       esw_offloads_unload_reps(esw, nvports);
-       esw_destroy_vport_rx_group(esw);
-       esw_destroy_offloads_table(esw);
-       esw_destroy_offloads_fdb_tables(esw);
+       esw_offloads_unload_all_reps(esw, num_vfs);
+       esw_offloads_steering_cleanup(esw);
 }
 
 static int esw_mode_from_devlink(u16 mode, u16 *mlx5_mode)
@@ -1548,7 +1847,8 @@ static int mlx5_devlink_eswitch_check(struct devlink *devlink)
        if(!MLX5_ESWITCH_MANAGER(dev))
                return -EPERM;
 
-       if (dev->priv.eswitch->mode == SRIOV_NONE)
+       if (dev->priv.eswitch->mode == SRIOV_NONE &&
+           !mlx5_core_is_ecpf_esw_manager(dev))
                return -EOPNOTSUPP;
 
        return 0;
@@ -1760,47 +2060,45 @@ int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink, u8 *encap)
        return 0;
 }
 
-void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw,
-                                    int vport_index,
-                                    struct mlx5_eswitch_rep_if *__rep_if,
-                                    u8 rep_type)
+void mlx5_eswitch_register_vport_reps(struct mlx5_eswitch *esw,
+                                     struct mlx5_eswitch_rep_if *__rep_if,
+                                     u8 rep_type)
 {
-       struct mlx5_esw_offload *offloads = &esw->offloads;
        struct mlx5_eswitch_rep_if *rep_if;
+       struct mlx5_eswitch_rep *rep;
+       int i;
 
-       rep_if = &offloads->vport_reps[vport_index].rep_if[rep_type];
-
-       rep_if->load   = __rep_if->load;
-       rep_if->unload = __rep_if->unload;
-       rep_if->get_proto_dev = __rep_if->get_proto_dev;
-       rep_if->priv = __rep_if->priv;
+       mlx5_esw_for_all_reps(esw, i, rep) {
+               rep_if = &rep->rep_if[rep_type];
+               rep_if->load   = __rep_if->load;
+               rep_if->unload = __rep_if->unload;
+               rep_if->get_proto_dev = __rep_if->get_proto_dev;
+               rep_if->priv = __rep_if->priv;
 
-       rep_if->valid = true;
+               rep_if->state = REP_REGISTERED;
+       }
 }
-EXPORT_SYMBOL(mlx5_eswitch_register_vport_rep);
+EXPORT_SYMBOL(mlx5_eswitch_register_vport_reps);
 
-void mlx5_eswitch_unregister_vport_rep(struct mlx5_eswitch *esw,
-                                      int vport_index, u8 rep_type)
+void mlx5_eswitch_unregister_vport_reps(struct mlx5_eswitch *esw, u8 rep_type)
 {
-       struct mlx5_esw_offload *offloads = &esw->offloads;
+       u16 max_vf = mlx5_core_max_vfs(esw->dev);
        struct mlx5_eswitch_rep *rep;
+       int i;
 
-       rep = &offloads->vport_reps[vport_index];
-
-       if (esw->mode == SRIOV_OFFLOADS && esw->vports[vport_index].enabled)
-               rep->rep_if[rep_type].unload(rep);
+       if (esw->mode == SRIOV_OFFLOADS)
+               __unload_reps_all_vport(esw, max_vf, rep_type);
 
-       rep->rep_if[rep_type].valid = false;
+       mlx5_esw_for_all_reps(esw, i, rep)
+               rep->rep_if[rep_type].state = REP_UNREGISTERED;
 }
-EXPORT_SYMBOL(mlx5_eswitch_unregister_vport_rep);
+EXPORT_SYMBOL(mlx5_eswitch_unregister_vport_reps);
 
 void *mlx5_eswitch_get_uplink_priv(struct mlx5_eswitch *esw, u8 rep_type)
 {
-#define UPLINK_REP_INDEX 0
-       struct mlx5_esw_offload *offloads = &esw->offloads;
        struct mlx5_eswitch_rep *rep;
 
-       rep = &offloads->vport_reps[UPLINK_REP_INDEX];
+       rep = mlx5_eswitch_get_rep(esw, MLX5_VPORT_UPLINK);
        return rep->rep_if[rep_type].priv;
 }
 
@@ -1808,15 +2106,11 @@ void *mlx5_eswitch_get_proto_dev(struct mlx5_eswitch *esw,
                                 int vport,
                                 u8 rep_type)
 {
-       struct mlx5_esw_offload *offloads = &esw->offloads;
        struct mlx5_eswitch_rep *rep;
 
-       if (vport == FDB_UPLINK_VPORT)
-               vport = UPLINK_REP_INDEX;
-
-       rep = &offloads->vport_reps[vport];
+       rep = mlx5_eswitch_get_rep(esw, vport);
 
-       if (rep->rep_if[rep_type].valid &&
+       if (rep->rep_if[rep_type].state == REP_LOADED &&
            rep->rep_if[rep_type].get_proto_dev)
                return rep->rep_if[rep_type].get_proto_dev(rep);
        return NULL;
@@ -1825,13 +2119,13 @@ EXPORT_SYMBOL(mlx5_eswitch_get_proto_dev);
 
 void *mlx5_eswitch_uplink_get_proto_dev(struct mlx5_eswitch *esw, u8 rep_type)
 {
-       return mlx5_eswitch_get_proto_dev(esw, UPLINK_REP_INDEX, rep_type);
+       return mlx5_eswitch_get_proto_dev(esw, MLX5_VPORT_UPLINK, rep_type);
 }
 EXPORT_SYMBOL(mlx5_eswitch_uplink_get_proto_dev);
 
 struct mlx5_eswitch_rep *mlx5_eswitch_vport_rep(struct mlx5_eswitch *esw,
                                                int vport)
 {
-       return &esw->offloads.vport_reps[vport];
+       return mlx5_eswitch_get_rep(esw, vport);
 }
 EXPORT_SYMBOL(mlx5_eswitch_vport_rep);
index fbc42b7..5d5864e 100644 (file)
@@ -103,6 +103,8 @@ static const char *eqe_type_str(u8 type)
                return "MLX5_EVENT_TYPE_STALL_EVENT";
        case MLX5_EVENT_TYPE_CMD:
                return "MLX5_EVENT_TYPE_CMD";
+       case MLX5_EVENT_TYPE_HOST_PARAMS_CHANGE:
+               return "MLX5_EVENT_TYPE_HOST_PARAMS_CHANGE";
        case MLX5_EVENT_TYPE_PAGE_REQUEST:
                return "MLX5_EVENT_TYPE_PAGE_REQUEST";
        case MLX5_EVENT_TYPE_PAGE_FAULT:
@@ -211,11 +213,10 @@ static int port_module(struct notifier_block *nb, unsigned long type, void *data
        enum port_module_event_status_type module_status;
        enum port_module_event_error_type error_type;
        struct mlx5_eqe_port_module *module_event_eqe;
-       const char *status_str, *error_str;
+       const char *status_str;
        u8 module_num;
 
        module_event_eqe = &eqe->data.port_module;
-       module_num = module_event_eqe->module;
        module_status = module_event_eqe->module_status &
                        PORT_MODULE_EVENT_MODULE_STATUS_MASK;
        error_type = module_event_eqe->error_type &
@@ -223,25 +224,27 @@ static int port_module(struct notifier_block *nb, unsigned long type, void *data
 
        if (module_status < MLX5_MODULE_STATUS_NUM)
                events->pme_stats.status_counters[module_status]++;
-       status_str = mlx5_pme_status_to_string(module_status);
 
-       if (module_status == MLX5_MODULE_STATUS_ERROR) {
+       if (module_status == MLX5_MODULE_STATUS_ERROR)
                if (error_type < MLX5_MODULE_EVENT_ERROR_NUM)
                        events->pme_stats.error_counters[error_type]++;
-               error_str = mlx5_pme_error_to_string(error_type);
-       }
 
        if (!printk_ratelimit())
                return NOTIFY_OK;
 
-       if (module_status == MLX5_MODULE_STATUS_ERROR)
+       module_num = module_event_eqe->module;
+       status_str = mlx5_pme_status_to_string(module_status);
+       if (module_status == MLX5_MODULE_STATUS_ERROR) {
+               const char *error_str = mlx5_pme_error_to_string(error_type);
+
                mlx5_core_err(events->dev,
                              "Port module event[error]: module %u, %s, %s\n",
                              module_num, status_str, error_str);
-       else
+       } else {
                mlx5_core_info(events->dev,
                               "Port module event: module %u, %s\n",
                               module_num, status_str);
+       }
 
        return NOTIFY_OK;
 }
index b2df7c3..f2cfa01 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <linux/mutex.h>
 #include <linux/mlx5/driver.h>
+#include <linux/mlx5/vport.h>
 #include <linux/mlx5/eswitch.h>
 
 #include "mlx5_core.h"
@@ -619,7 +620,8 @@ static struct mlx5_flow_group *alloc_flow_group(struct mlx5_flow_steering *steer
        if (ret) {
                kmem_cache_free(steering->fgs_cache, fg);
                return ERR_PTR(ret);
-}
+       }
+
        ida_init(&fg->fte_allocator);
        fg->mask.match_criteria_enable = match_criteria_enable;
        memcpy(&fg->mask.match_criteria, match_criteria,
index 196c073..cb9fa34 100644 (file)
@@ -103,7 +103,7 @@ void mlx5_enter_error_state(struct mlx5_core_dev *dev, bool force)
        mlx5_core_err(dev, "start\n");
        if (pci_channel_offline(dev->pdev) || in_fatal(dev) || force) {
                dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
-               mlx5_cmd_trigger_completions(dev);
+               mlx5_cmd_flush(dev);
        }
 
        mlx5_notifier_call_chain(dev->priv.events, MLX5_DEV_EVENT_SYS_ERROR, (void *)1);
index 3a6baed..04c5aca 100644 (file)
@@ -343,6 +343,11 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)
                roce_lag = !mlx5_sriov_is_enabled(dev0) &&
                           !mlx5_sriov_is_enabled(dev1);
 
+#ifdef CONFIG_MLX5_ESWITCH
+               roce_lag &= dev0->priv.eswitch->mode == SRIOV_NONE &&
+                           dev1->priv.eswitch->mode == SRIOV_NONE;
+#endif
+
                if (roce_lag)
                        mlx5_lag_remove_ib_devices(ldev);
 
@@ -616,6 +621,27 @@ void mlx5_lag_add(struct mlx5_core_dev *dev, struct net_device *netdev)
        }
 }
 
+int mlx5_lag_get_pf_num(struct mlx5_core_dev *dev, int *pf_num)
+{
+       struct mlx5_lag *ldev;
+       int n;
+
+       ldev = mlx5_lag_dev_get(dev);
+       if (!ldev) {
+               mlx5_core_warn(dev, "no lag device, can't get pf num\n");
+               return -EINVAL;
+       }
+
+       for (n = 0; n < MLX5_MAX_PORTS; n++)
+               if (ldev->pf[n].dev == dev) {
+                       *pf_num = n;
+                       return 0;
+               }
+
+       mlx5_core_warn(dev, "wasn't able to locate pf in the lag device\n");
+       return -EINVAL;
+}
+
 /* Must be called with intf_mutex held */
 void mlx5_lag_remove(struct mlx5_core_dev *dev)
 {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mad.c b/drivers/net/ethernet/mellanox/mlx5/core/mad.c
deleted file mode 100644 (file)
index 3a3b000..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2013-2015, 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mlx5/driver.h>
-#include <linux/mlx5/cmd.h>
-#include "mlx5_core.h"
-
-int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, const void *inb, void *outb,
-                     u16 opmod, u8 port)
-{
-       int outlen = MLX5_ST_SZ_BYTES(mad_ifc_out);
-       int inlen = MLX5_ST_SZ_BYTES(mad_ifc_in);
-       int err = -ENOMEM;
-       void *data;
-       void *resp;
-       u32 *out;
-       u32 *in;
-
-       in = kzalloc(inlen, GFP_KERNEL);
-       out = kzalloc(outlen, GFP_KERNEL);
-       if (!in || !out)
-               goto out;
-
-       MLX5_SET(mad_ifc_in, in, opcode, MLX5_CMD_OP_MAD_IFC);
-       MLX5_SET(mad_ifc_in, in, op_mod, opmod);
-       MLX5_SET(mad_ifc_in, in, port, port);
-
-       data = MLX5_ADDR_OF(mad_ifc_in, in, mad);
-       memcpy(data, inb, MLX5_FLD_SZ_BYTES(mad_ifc_in, mad));
-
-       err = mlx5_cmd_exec(dev, in, inlen, out, outlen);
-       if (err)
-               goto out;
-
-       resp = MLX5_ADDR_OF(mad_ifc_out, out, response_mad_packet);
-       memcpy(outb, resp,
-              MLX5_FLD_SZ_BYTES(mad_ifc_out, response_mad_packet));
-
-out:
-       kfree(out);
-       kfree(in);
-       return err;
-}
-EXPORT_SYMBOL_GPL(mlx5_core_mad_ifc);
index be81b31..40d591c 100644 (file)
@@ -65,6 +65,7 @@
 #include "lib/vxlan.h"
 #include "lib/devcom.h"
 #include "diag/fw_tracer.h"
+#include "ecpf.h"
 
 MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
 MODULE_DESCRIPTION("Mellanox 5th generation network adapters (ConnectX series) core driver");
@@ -459,6 +460,50 @@ static int handle_hca_cap_atomic(struct mlx5_core_dev *dev)
        return err;
 }
 
+static int handle_hca_cap_odp(struct mlx5_core_dev *dev)
+{
+       void *set_hca_cap;
+       void *set_ctx;
+       int set_sz;
+       int err;
+
+       if (!MLX5_CAP_GEN(dev, pg))
+               return 0;
+
+       err = mlx5_core_get_caps(dev, MLX5_CAP_ODP);
+       if (err)
+               return err;
+
+       if (!(MLX5_CAP_ODP_MAX(dev, ud_odp_caps.srq_receive) ||
+             MLX5_CAP_ODP_MAX(dev, rc_odp_caps.srq_receive) ||
+             MLX5_CAP_ODP_MAX(dev, xrc_odp_caps.srq_receive)))
+               return 0;
+
+       set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in);
+       set_ctx = kzalloc(set_sz, GFP_KERNEL);
+       if (!set_ctx)
+               return -ENOMEM;
+
+       set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx, capability);
+       memcpy(set_hca_cap, dev->caps.hca_cur[MLX5_CAP_ODP],
+              MLX5_ST_SZ_BYTES(odp_cap));
+
+       /* set ODP SRQ support for RC/UD and XRC transports */
+       MLX5_SET(odp_cap, set_hca_cap, ud_odp_caps.srq_receive,
+                MLX5_CAP_ODP_MAX(dev, ud_odp_caps.srq_receive));
+
+       MLX5_SET(odp_cap, set_hca_cap, rc_odp_caps.srq_receive,
+                MLX5_CAP_ODP_MAX(dev, rc_odp_caps.srq_receive));
+
+       MLX5_SET(odp_cap, set_hca_cap, xrc_odp_caps.srq_receive,
+                MLX5_CAP_ODP_MAX(dev, xrc_odp_caps.srq_receive));
+
+       err = set_caps(dev, set_ctx, set_sz, MLX5_SET_HCA_CAP_OP_MOD_ODP);
+
+       kfree(set_ctx);
+       return err;
+}
+
 static int handle_hca_cap(struct mlx5_core_dev *dev)
 {
        void *set_ctx = NULL;
@@ -567,6 +612,8 @@ int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id)
 
        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,
+                dev->caps.embedded_cpu);
        return mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
 }
 
@@ -577,6 +624,8 @@ int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id)
 
        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,
+                dev->caps.embedded_cpu);
        return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
 }
 
@@ -693,6 +742,11 @@ static int mlx5_pci_init(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
                goto err_clr_master;
        }
 
+       if (pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP32) &&
+           pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP64) &&
+           pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP128))
+               mlx5_core_dbg(dev, "Enabling pci atomics failed\n");
+
        dev->iseg_base = pci_resource_start(dev->pdev, 0);
        dev->iseg = ioremap(dev->iseg_base, sizeof(*dev->iseg));
        if (!dev->iseg) {
@@ -849,6 +903,7 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
        struct pci_dev *pdev = dev->pdev;
        int err;
 
+       dev->caps.embedded_cpu = mlx5_read_embedded_cpu(dev);
        mutex_lock(&dev->intf_state_mutex);
        if (test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state)) {
                dev_warn(&dev->pdev->dev, "%s: interface is up, NOP\n",
@@ -926,6 +981,12 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
                goto reclaim_boot_pages;
        }
 
+       err = handle_hca_cap_odp(dev);
+       if (err) {
+               dev_err(&pdev->dev, "handle_hca_cap_odp failed\n");
+               goto reclaim_boot_pages;
+       }
+
        err = mlx5_satisfy_startup_pages(dev, 0);
        if (err) {
                dev_err(&pdev->dev, "failed to allocate init pages\n");
@@ -1014,6 +1075,12 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
                goto err_sriov;
        }
 
+       err = mlx5_ec_init(dev);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to init embedded CPU\n");
+               goto err_ec;
+       }
+
        if (mlx5_device_registered(dev)) {
                mlx5_attach_device(dev);
        } else {
@@ -1031,6 +1098,9 @@ out:
        return 0;
 
 err_reg_dev:
+       mlx5_ec_cleanup(dev);
+
+err_ec:
        mlx5_sriov_detach(dev);
 
 err_sriov:
@@ -1105,6 +1175,7 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
        if (mlx5_device_registered(dev))
                mlx5_detach_device(dev);
 
+       mlx5_ec_cleanup(dev);
        mlx5_sriov_detach(dev);
        mlx5_cleanup_fs(dev);
        mlx5_accel_ipsec_cleanup(dev);
index c68dcea..9529cf9 100644 (file)
@@ -121,11 +121,12 @@ int mlx5_modify_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
                                       u32 modify_bitmask);
 int mlx5_destroy_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
                                        u32 element_id);
-int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev);
+int mlx5_wait_for_pages(struct mlx5_core_dev *dev, int *pages);
 u64 mlx5_read_internal_timer(struct mlx5_core_dev *dev,
                             struct ptp_system_timestamp *sts);
 
 void mlx5_cmd_trigger_completions(struct mlx5_core_dev *dev);
+void mlx5_cmd_flush(struct mlx5_core_dev *dev);
 int mlx5_cq_debugfs_init(struct mlx5_core_dev *dev);
 void mlx5_cq_debugfs_cleanup(struct mlx5_core_dev *dev);
 
@@ -187,6 +188,8 @@ static inline int mlx5_lag_is_lacp_owner(struct mlx5_core_dev *dev)
                    MLX5_CAP_GEN(dev, lag_master);
 }
 
+int mlx5_lag_get_pf_num(struct mlx5_core_dev *dev, int *pf_num);
+
 void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol);
 void mlx5_lag_update(struct mlx5_core_dev *dev);
 
index 0670165..ea744d8 100644 (file)
@@ -51,9 +51,10 @@ void mlx5_cleanup_mkey_table(struct mlx5_core_dev *dev)
 
 int mlx5_core_create_mkey_cb(struct mlx5_core_dev *dev,
                             struct mlx5_core_mkey *mkey,
-                            u32 *in, int inlen,
-                            u32 *out, int outlen,
-                            mlx5_cmd_cbk_t callback, void *context)
+                            struct mlx5_async_ctx *async_ctx, u32 *in,
+                            int inlen, u32 *out, int outlen,
+                            mlx5_async_cbk_t callback,
+                            struct mlx5_async_work *context)
 {
        struct mlx5_mkey_table *table = &dev->priv.mkey_table;
        u32 lout[MLX5_ST_SZ_DW(create_mkey_out)] = {0};
@@ -71,7 +72,7 @@ int mlx5_core_create_mkey_cb(struct mlx5_core_dev *dev,
        MLX5_SET(mkc, mkc, mkey_7_0, key);
 
        if (callback)
-               return mlx5_cmd_exec_cb(dev, in, inlen, out, outlen,
+               return mlx5_cmd_exec_cb(async_ctx, in, inlen, out, outlen,
                                        callback, context);
 
        err = mlx5_cmd_exec(dev, in, inlen, lout, sizeof(lout));
@@ -105,7 +106,7 @@ int mlx5_core_create_mkey(struct mlx5_core_dev *dev,
                          struct mlx5_core_mkey *mkey,
                          u32 *in, int inlen)
 {
-       return mlx5_core_create_mkey_cb(dev, mkey, in, inlen,
+       return mlx5_core_create_mkey_cb(dev, mkey, NULL, in, inlen,
                                        NULL, 0, NULL, NULL);
 }
 EXPORT_SYMBOL(mlx5_core_create_mkey);
index a83b517..4102538 100644 (file)
@@ -48,6 +48,7 @@ enum {
 struct mlx5_pages_req {
        struct mlx5_core_dev *dev;
        u16     func_id;
+       u8      ec_function;
        s32     npages;
        struct work_struct work;
 };
@@ -143,6 +144,7 @@ static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id,
        MLX5_SET(query_pages_in, in, op_mod, boot ?
                 MLX5_QUERY_PAGES_IN_OP_MOD_BOOT_PAGES :
                 MLX5_QUERY_PAGES_IN_OP_MOD_INIT_PAGES);
+       MLX5_SET(query_pages_in, in, embedded_cpu_function, mlx5_core_is_ecpf(dev));
 
        err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
        if (err)
@@ -253,7 +255,8 @@ err_mapping:
        return err;
 }
 
-static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id)
+static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id,
+                            bool ec_function)
 {
        u32 out[MLX5_ST_SZ_DW(manage_pages_out)] = {0};
        u32 in[MLX5_ST_SZ_DW(manage_pages_in)]   = {0};
@@ -262,6 +265,7 @@ static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id)
        MLX5_SET(manage_pages_in, in, opcode, MLX5_CMD_OP_MANAGE_PAGES);
        MLX5_SET(manage_pages_in, in, op_mod, MLX5_PAGES_CANT_GIVE);
        MLX5_SET(manage_pages_in, in, function_id, func_id);
+       MLX5_SET(manage_pages_in, in, embedded_cpu_function, ec_function);
 
        err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
        if (err)
@@ -270,7 +274,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)
+                     int notify_fail, bool ec_function)
 {
        u32 out[MLX5_ST_SZ_DW(manage_pages_out)] = {0};
        int inlen = MLX5_ST_SZ_BYTES(manage_pages_in);
@@ -305,6 +309,7 @@ retry:
        MLX5_SET(manage_pages_in, in, op_mod, MLX5_PAGES_GIVE);
        MLX5_SET(manage_pages_in, in, function_id, func_id);
        MLX5_SET(manage_pages_in, in, input_num_entries, npages);
+       MLX5_SET(manage_pages_in, in, embedded_cpu_function, ec_function);
 
        err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
        if (err) {
@@ -316,8 +321,11 @@ retry:
        dev->priv.fw_pages += npages;
        if (func_id)
                dev->priv.vfs_pages += npages;
+       else if (mlx5_core_is_ecpf(dev) && !ec_function)
+               dev->priv.peer_pf_pages += npages;
 
-       mlx5_core_dbg(dev, "err %d\n", err);
+       mlx5_core_dbg(dev, "npages %d, ec_function %d, func_id 0x%x, err %d\n",
+                     npages, ec_function, func_id, err);
 
        kvfree(in);
        return 0;
@@ -328,7 +336,7 @@ out_4k:
 out_free:
        kvfree(in);
        if (notify_fail)
-               page_notify_fail(dev, func_id);
+               page_notify_fail(dev, func_id, ec_function);
        return err;
 }
 
@@ -364,7 +372,7 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev,
 }
 
 static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
-                        int *nclaimed)
+                        int *nclaimed, bool ec_function)
 {
        int outlen = MLX5_ST_SZ_BYTES(manage_pages_out);
        u32 in[MLX5_ST_SZ_DW(manage_pages_in)] = {0};
@@ -385,6 +393,7 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
        MLX5_SET(manage_pages_in, in, op_mod, MLX5_PAGES_TAKE);
        MLX5_SET(manage_pages_in, in, function_id, func_id);
        MLX5_SET(manage_pages_in, in, input_num_entries, npages);
+       MLX5_SET(manage_pages_in, in, embedded_cpu_function, ec_function);
 
        mlx5_core_dbg(dev, "npages %d, outlen %d\n", npages, outlen);
        err = reclaim_pages_cmd(dev, in, sizeof(in), out, outlen);
@@ -410,6 +419,8 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
        dev->priv.fw_pages -= num_claimed;
        if (func_id)
                dev->priv.vfs_pages -= num_claimed;
+       else if (mlx5_core_is_ecpf(dev) && !ec_function)
+               dev->priv.peer_pf_pages -= num_claimed;
 
 out_free:
        kvfree(out);
@@ -423,9 +434,10 @@ static void pages_work_handler(struct work_struct *work)
        int err = 0;
 
        if (req->npages < 0)
-               err = reclaim_pages(dev, req->func_id, -1 * req->npages, NULL);
+               err = reclaim_pages(dev, req->func_id, -1 * req->npages, NULL,
+                                   req->ec_function);
        else if (req->npages > 0)
-               err = give_pages(dev, req->func_id, req->npages, 1);
+               err = give_pages(dev, req->func_id, req->npages, 1, req->ec_function);
 
        if (err)
                mlx5_core_warn(dev, "%s fail %d\n",
@@ -434,6 +446,10 @@ static void pages_work_handler(struct work_struct *work)
        kfree(req);
 }
 
+enum {
+       EC_FUNCTION_MASK = 0x8000,
+};
+
 static int req_pages_handler(struct notifier_block *nb,
                             unsigned long type, void *data)
 {
@@ -441,6 +457,7 @@ static int req_pages_handler(struct notifier_block *nb,
        struct mlx5_core_dev *dev;
        struct mlx5_priv *priv;
        struct mlx5_eqe *eqe;
+       bool ec_function;
        u16 func_id;
        s32 npages;
 
@@ -450,6 +467,7 @@ static int req_pages_handler(struct notifier_block *nb,
 
        func_id = be16_to_cpu(eqe->data.req_pages.func_id);
        npages  = be32_to_cpu(eqe->data.req_pages.num_pages);
+       ec_function = be16_to_cpu(eqe->data.req_pages.ec_function) & EC_FUNCTION_MASK;
        mlx5_core_dbg(dev, "page request for func 0x%x, npages %d\n",
                      func_id, npages);
        req = kzalloc(sizeof(*req), GFP_ATOMIC);
@@ -461,6 +479,7 @@ static int req_pages_handler(struct notifier_block *nb,
        req->dev = dev;
        req->func_id = func_id;
        req->npages = npages;
+       req->ec_function = ec_function;
        INIT_WORK(&req->work, pages_work_handler);
        queue_work(dev->priv.pg_wq, &req->work);
        return NOTIFY_OK;
@@ -479,7 +498,7 @@ int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot)
        mlx5_core_dbg(dev, "requested %d %s pages for func_id 0x%x\n",
                      npages, boot ? "boot" : "init", func_id);
 
-       return give_pages(dev, func_id, npages, 0);
+       return give_pages(dev, func_id, npages, 0, mlx5_core_is_ecpf(dev));
 }
 
 enum {
@@ -513,7 +532,7 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
                        fwp = rb_entry(p, struct fw_page, rb_node);
                        err = reclaim_pages(dev, fwp->func_id,
                                            optimal_reclaimed_pages(),
-                                           &nclaimed);
+                                           &nclaimed, mlx5_core_is_ecpf(dev));
 
                        if (err) {
                                mlx5_core_warn(dev, "failed reclaiming pages (%d)\n",
@@ -535,6 +554,9 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
        WARN(dev->priv.vfs_pages,
             "VFs FW pages counter is %d after reclaiming all pages\n",
             dev->priv.vfs_pages);
+       WARN(dev->priv.peer_pf_pages,
+            "Peer PF FW pages counter is %d after reclaiming all pages\n",
+            dev->priv.peer_pf_pages);
 
        return 0;
 }
@@ -567,10 +589,10 @@ void mlx5_pagealloc_stop(struct mlx5_core_dev *dev)
        flush_workqueue(dev->priv.pg_wq);
 }
 
-int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev)
+int mlx5_wait_for_pages(struct mlx5_core_dev *dev, int *pages)
 {
        unsigned long end = jiffies + msecs_to_jiffies(MAX_RECLAIM_VFS_PAGES_TIME_MSECS);
-       int prev_vfs_pages = dev->priv.vfs_pages;
+       int prev_pages = *pages;
 
        /* In case of internal error we will free the pages manually later */
        if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
@@ -578,16 +600,16 @@ int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev)
                return 0;
        }
 
-       mlx5_core_dbg(dev, "Waiting for %d pages from %s\n", prev_vfs_pages,
+       mlx5_core_dbg(dev, "Waiting for %d pages from %s\n", prev_pages,
                      dev->priv.name);
-       while (dev->priv.vfs_pages) {
+       while (*pages) {
                if (time_after(jiffies, end)) {
-                       mlx5_core_warn(dev, "aborting while there are %d pending pages\n", dev->priv.vfs_pages);
+                       mlx5_core_warn(dev, "aborting while there are %d pending pages\n", *pages);
                        return -ETIMEDOUT;
                }
-               if (dev->priv.vfs_pages < prev_vfs_pages) {
+               if (*pages < prev_pages) {
                        end = jiffies + msecs_to_jiffies(MAX_RECLAIM_VFS_PAGES_TIME_MSECS);
-                       prev_vfs_pages = dev->priv.vfs_pages;
+                       prev_pages = *pages;
                }
                msleep(50);
        }
index 2b82f35..b815428 100644 (file)
  * SOFTWARE.
  */
 
-#include <linux/module.h>
-#include <linux/mlx5/driver.h>
 #include <linux/mlx5/port.h>
-#include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
 
 int mlx5_core_access_reg(struct mlx5_core_dev *dev, void *data_in,
@@ -157,44 +154,6 @@ int mlx5_set_port_beacon(struct mlx5_core_dev *dev, u16 beacon_duration)
                                    sizeof(out), MLX5_REG_MLCR, 0, 1);
 }
 
-int mlx5_query_port_proto_cap(struct mlx5_core_dev *dev,
-                             u32 *proto_cap, int proto_mask)
-{
-       u32 out[MLX5_ST_SZ_DW(ptys_reg)];
-       int err;
-
-       err = mlx5_query_port_ptys(dev, out, sizeof(out), proto_mask, 1);
-       if (err)
-               return err;
-
-       if (proto_mask == MLX5_PTYS_EN)
-               *proto_cap = MLX5_GET(ptys_reg, out, eth_proto_capability);
-       else
-               *proto_cap = MLX5_GET(ptys_reg, out, ib_proto_capability);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(mlx5_query_port_proto_cap);
-
-int mlx5_query_port_proto_admin(struct mlx5_core_dev *dev,
-                               u32 *proto_admin, int proto_mask)
-{
-       u32 out[MLX5_ST_SZ_DW(ptys_reg)];
-       int err;
-
-       err = mlx5_query_port_ptys(dev, out, sizeof(out), proto_mask, 1);
-       if (err)
-               return err;
-
-       if (proto_mask == MLX5_PTYS_EN)
-               *proto_admin = MLX5_GET(ptys_reg, out, eth_proto_admin);
-       else
-               *proto_admin = MLX5_GET(ptys_reg, out, ib_proto_admin);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(mlx5_query_port_proto_admin);
-
 int mlx5_query_port_link_width_oper(struct mlx5_core_dev *dev,
                                    u8 *link_width_oper, u8 local_port)
 {
@@ -211,23 +170,6 @@ int mlx5_query_port_link_width_oper(struct mlx5_core_dev *dev,
 }
 EXPORT_SYMBOL_GPL(mlx5_query_port_link_width_oper);
 
-int mlx5_query_port_eth_proto_oper(struct mlx5_core_dev *dev,
-                                  u32 *proto_oper, u8 local_port)
-{
-       u32 out[MLX5_ST_SZ_DW(ptys_reg)];
-       int err;
-
-       err = mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN,
-                                  local_port);
-       if (err)
-               return err;
-
-       *proto_oper = MLX5_GET(ptys_reg, out, eth_proto_oper);
-
-       return 0;
-}
-EXPORT_SYMBOL(mlx5_query_port_eth_proto_oper);
-
 int mlx5_query_port_ib_proto_oper(struct mlx5_core_dev *dev,
                                  u8 *proto_oper, u8 local_port)
 {
@@ -245,35 +187,6 @@ int mlx5_query_port_ib_proto_oper(struct mlx5_core_dev *dev,
 }
 EXPORT_SYMBOL(mlx5_query_port_ib_proto_oper);
 
-int mlx5_set_port_ptys(struct mlx5_core_dev *dev, bool an_disable,
-                      u32 proto_admin, int proto_mask)
-{
-       u32 out[MLX5_ST_SZ_DW(ptys_reg)];
-       u32 in[MLX5_ST_SZ_DW(ptys_reg)];
-       u8 an_disable_admin;
-       u8 an_disable_cap;
-       u8 an_status;
-
-       mlx5_query_port_autoneg(dev, proto_mask, &an_status,
-                               &an_disable_cap, &an_disable_admin);
-       if (!an_disable_cap && an_disable)
-               return -EPERM;
-
-       memset(in, 0, sizeof(in));
-
-       MLX5_SET(ptys_reg, in, local_port, 1);
-       MLX5_SET(ptys_reg, in, an_disable_admin, an_disable);
-       MLX5_SET(ptys_reg, in, proto_mask, proto_mask);
-       if (proto_mask == MLX5_PTYS_EN)
-               MLX5_SET(ptys_reg, in, eth_proto_admin, proto_admin);
-       else
-               MLX5_SET(ptys_reg, in, ib_proto_admin, proto_admin);
-
-       return mlx5_core_access_reg(dev, in, sizeof(in), out,
-                                   sizeof(out), MLX5_REG_PTYS, 0, 1);
-}
-EXPORT_SYMBOL_GPL(mlx5_set_port_ptys);
-
 /* This function should be used after setting a port register only */
 void mlx5_toggle_port_link(struct mlx5_core_dev *dev)
 {
@@ -606,25 +519,6 @@ int mlx5_query_port_pfc(struct mlx5_core_dev *dev, u8 *pfc_en_tx, u8 *pfc_en_rx)
 }
 EXPORT_SYMBOL_GPL(mlx5_query_port_pfc);
 
-void mlx5_query_port_autoneg(struct mlx5_core_dev *dev, int proto_mask,
-                            u8 *an_status,
-                            u8 *an_disable_cap, u8 *an_disable_admin)
-{
-       u32 out[MLX5_ST_SZ_DW(ptys_reg)];
-
-       *an_status = 0;
-       *an_disable_cap = 0;
-       *an_disable_admin = 0;
-
-       if (mlx5_query_port_ptys(dev, out, sizeof(out), proto_mask, 1))
-               return;
-
-       *an_status = MLX5_GET(ptys_reg, out, an_status);
-       *an_disable_cap = MLX5_GET(ptys_reg, out, an_disable_cap);
-       *an_disable_admin = MLX5_GET(ptys_reg, out, an_disable_admin);
-}
-EXPORT_SYMBOL_GPL(mlx5_query_port_autoneg);
-
 int mlx5_max_tc(struct mlx5_core_dev *mdev)
 {
        u8 num_tc = MLX5_CAP_GEN(mdev, max_tc) ? : 8;
index 388f205..370ca94 100644 (file)
@@ -44,14 +44,15 @@ static struct mlx5_core_rsc_common *
 mlx5_get_rsc(struct mlx5_qp_table *table, u32 rsn)
 {
        struct mlx5_core_rsc_common *common;
+       unsigned long flags;
 
-       spin_lock(&table->lock);
+       spin_lock_irqsave(&table->lock, flags);
 
        common = radix_tree_lookup(&table->tree, rsn);
        if (common)
                atomic_inc(&common->refcount);
 
-       spin_unlock(&table->lock);
+       spin_unlock_irqrestore(&table->lock, flags);
 
        return common;
 }
index 6e17803..7b23fa8 100644 (file)
@@ -147,7 +147,7 @@ out:
        if (MLX5_ESWITCH_MANAGER(dev))
                mlx5_eswitch_disable_sriov(dev->priv.eswitch);
 
-       if (mlx5_wait_for_vf_pages(dev))
+       if (mlx5_wait_for_pages(dev, &dev->priv.vfs_pages))
                mlx5_core_warn(dev, "timeout reclaiming VFs pages\n");
 }
 
index 9b150ce..ef95fec 100644 (file)
@@ -64,7 +64,7 @@ u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
 }
 
 int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
-                                 u16 vport, u8 state)
+                                 u16 vport, u8 other_vport, u8 state)
 {
        u32 in[MLX5_ST_SZ_DW(modify_vport_state_in)]   = {0};
        u32 out[MLX5_ST_SZ_DW(modify_vport_state_out)] = {0};
@@ -73,8 +73,7 @@ int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
                 MLX5_CMD_OP_MODIFY_VPORT_STATE);
        MLX5_SET(modify_vport_state_in, in, op_mod, opmod);
        MLX5_SET(modify_vport_state_in, in, vport_number, vport);
-       if (vport)
-               MLX5_SET(modify_vport_state_in, in, other_vport, 1);
+       MLX5_SET(modify_vport_state_in, in, other_vport, other_vport);
        MLX5_SET(modify_vport_state_in, in, admin_state, state);
 
        return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
@@ -255,7 +254,7 @@ int mlx5_modify_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 mtu)
 EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mtu);
 
 int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
-                                 u32 vport,
+                                 u16 vport,
                                  enum mlx5_list_type list_type,
                                  u8 addr_list[][ETH_ALEN],
                                  int *list_size)
@@ -373,7 +372,7 @@ int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev,
 EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mac_list);
 
 int mlx5_query_nic_vport_vlans(struct mlx5_core_dev *dev,
-                              u32 vport,
+                              u16 vport,
                               u16 vlans[],
                               int *size)
 {
@@ -526,7 +525,7 @@ int mlx5_query_nic_vport_node_guid(struct mlx5_core_dev *mdev, u64 *node_guid)
 EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_node_guid);
 
 int mlx5_modify_nic_vport_node_guid(struct mlx5_core_dev *mdev,
-                                   u32 vport, u64 node_guid)
+                                   u16 vport, u64 node_guid)
 {
        int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
        void *nic_vport_context;
@@ -827,7 +826,7 @@ int mlx5_query_hca_vport_node_guid(struct mlx5_core_dev *dev,
 EXPORT_SYMBOL_GPL(mlx5_query_hca_vport_node_guid);
 
 int mlx5_query_nic_vport_promisc(struct mlx5_core_dev *mdev,
-                                u32 vport,
+                                u16 vport,
                                 int *promisc_uc,
                                 int *promisc_mc,
                                 int *promisc_all)
@@ -1057,7 +1056,7 @@ free:
 EXPORT_SYMBOL_GPL(mlx5_core_query_vport_counter);
 
 int mlx5_query_vport_down_stats(struct mlx5_core_dev *mdev, u16 vport,
-                               u64 *rx_discard_vport_down,
+                               u8 other_vport, u64 *rx_discard_vport_down,
                                u64 *tx_discard_vport_down)
 {
        u32 out[MLX5_ST_SZ_DW(query_vnic_env_out)] = {0};
@@ -1068,8 +1067,7 @@ int mlx5_query_vport_down_stats(struct mlx5_core_dev *mdev, u16 vport,
                 MLX5_CMD_OP_QUERY_VNIC_ENV);
        MLX5_SET(query_vnic_env_in, in, op_mod, 0);
        MLX5_SET(query_vnic_env_in, in, vport_number, vport);
-       if (vport)
-               MLX5_SET(query_vnic_env_in, in, other_vport, 1);
+       MLX5_SET(query_vnic_env_in, in, other_vport, other_vport);
 
        err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
        if (err)
index bbf45f1..a01d155 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_MLXSW_CORE)       += mlxsw_core.o
 mlxsw_core-objs                        := core.o core_acl_flex_keys.o \
-                                  core_acl_flex_actions.o
+                                  core_acl_flex_actions.o core_env.o
 mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o
 mlxsw_core-$(CONFIG_MLXSW_CORE_THERMAL) += core_thermal.o
 obj-$(CONFIG_MLXSW_PCI)                += mlxsw_pci.o
index ddedf8a..b505d38 100644 (file)
@@ -1062,6 +1062,9 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
                        goto err_driver_init;
        }
 
+       if (mlxsw_driver->params_register && !reload)
+               devlink_params_publish(devlink);
+
        return 0;
 
 err_driver_init:
@@ -1131,6 +1134,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
                        return;
        }
 
+       if (mlxsw_core->driver->params_unregister && !reload)
+               devlink_params_unpublish(devlink);
        if (mlxsw_core->driver->fini)
                mlxsw_core->driver->fini(mlxsw_core);
        mlxsw_thermal_fini(mlxsw_core->thermal);
@@ -1460,13 +1465,17 @@ static int mlxsw_reg_trans_wait(struct mlxsw_reg_trans *trans)
        if (trans->retries)
                dev_warn(mlxsw_core->bus_info->dev, "EMAD retries (%d/%d) (tid=%llx)\n",
                         trans->retries, MLXSW_EMAD_MAX_RETRY, trans->tid);
-       if (err)
+       if (err) {
                dev_err(mlxsw_core->bus_info->dev, "EMAD reg access failed (tid=%llx,reg_id=%x(%s),type=%s,status=%x(%s))\n",
                        trans->tid, trans->reg->id,
                        mlxsw_reg_id_str(trans->reg->id),
                        mlxsw_core_reg_access_type_str(trans->type),
                        trans->emad_status,
                        mlxsw_emad_op_tlv_status_str(trans->emad_status));
+               trace_devlink_hwerr(priv_to_devlink(mlxsw_core),
+                                   trans->emad_status,
+                                   mlxsw_emad_op_tlv_status_str(trans->emad_status));
+       }
 
        list_del(&trans->bulk_list);
        kfree_rcu(trans, rcu);
index 4e114f3..cd0c6aa 100644 (file)
@@ -344,6 +344,7 @@ struct mlxsw_bus_info {
        struct mlxsw_fw_rev fw_rev;
        u8 vsd[MLXSW_CMD_BOARDINFO_VSD_LEN];
        u8 psid[MLXSW_CMD_BOARDINFO_PSID_LEN];
+       u8 low_frequency;
 };
 
 struct mlxsw_hwmon;
@@ -394,4 +395,9 @@ static inline void mlxsw_thermal_fini(struct mlxsw_thermal *thermal)
 
 #endif
 
+enum mlxsw_devlink_param_id {
+       MLXSW_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
+       MLXSW_DEVLINK_PARAM_ID_ACL_REGION_REHASH_INTERVAL,
+};
+
 #endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c
new file mode 100644 (file)
index 0000000..160d6cd
--- /dev/null
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+
+#include "core.h"
+#include "core_env.h"
+#include "item.h"
+#include "reg.h"
+
+static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
+                                         bool *qsfp)
+{
+       char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
+       char mcia_pl[MLXSW_REG_MCIA_LEN];
+       u8 ident;
+       int err;
+
+       mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,
+                           MLXSW_REG_MCIA_I2C_ADDR_LOW);
+       err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
+       if (err)
+               return err;
+       mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
+       ident = eeprom_tmp[0];
+       switch (ident) {
+       case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
+               *qsfp = false;
+               break;
+       case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP: /* fall-through */
+       case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
+       case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28: /* fall-through */
+       case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
+               *qsfp = true;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
+                                        int off, int *temp)
+{
+       char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
+       union {
+               u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
+               u16 temp;
+       } temp_thresh;
+       char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
+       char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};
+       u16 module_temp;
+       bool qsfp;
+       int err;
+
+       mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
+                           1);
+       err = mlxsw_reg_query(core, MLXSW_REG(mtbr), mtbr_pl);
+       if (err)
+               return err;
+
+       /* Don't read temperature thresholds for module with no valid info. */
+       mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &module_temp, NULL);
+       switch (module_temp) {
+       case MLXSW_REG_MTBR_BAD_SENS_INFO: /* fall-through */
+       case MLXSW_REG_MTBR_NO_CONN: /* fall-through */
+       case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */
+       case MLXSW_REG_MTBR_INDEX_NA:
+               *temp = 0;
+               return 0;
+       default:
+               /* Do not consider thresholds for zero temperature. */
+               if (!MLXSW_REG_MTMP_TEMP_TO_MC(module_temp)) {
+                       *temp = 0;
+                       return 0;
+               }
+               break;
+       }
+
+       /* Read Free Side Device Temperature Thresholds from page 03h
+        * (MSB at lower byte address).
+        * Bytes:
+        * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
+        * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
+        * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
+        * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
+        */
+
+       /* Validate module identifier value. */
+       err = mlxsw_env_validate_cable_ident(core, module, &qsfp);
+       if (err)
+               return err;
+
+       if (qsfp)
+               mlxsw_reg_mcia_pack(mcia_pl, module, 0,
+                                   MLXSW_REG_MCIA_TH_PAGE_NUM,
+                                   MLXSW_REG_MCIA_TH_PAGE_OFF + off,
+                                   MLXSW_REG_MCIA_TH_ITEM_SIZE,
+                                   MLXSW_REG_MCIA_I2C_ADDR_LOW);
+       else
+               mlxsw_reg_mcia_pack(mcia_pl, module, 0,
+                                   MLXSW_REG_MCIA_PAGE0_LO,
+                                   off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
+                                   MLXSW_REG_MCIA_I2C_ADDR_HIGH);
+
+       err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
+       memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
+       *temp = temp_thresh.temp * 1000;
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.h b/drivers/net/ethernet/mellanox/mlxsw/core_env.h
new file mode 100644 (file)
index 0000000..6dbdf63
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#ifndef _MLXSW_CORE_ENV_H
+#define _MLXSW_CORE_ENV_H
+
+int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
+                                        int off, int *temp);
+
+#endif
index e04e816..6956bbe 100644 (file)
@@ -7,8 +7,10 @@
 #include <linux/sysfs.h>
 #include <linux/hwmon.h>
 #include <linux/err.h>
+#include <linux/sfp.h>
 
 #include "core.h"
+#include "core_env.h"
 
 #define MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT 127
 #define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT * 4 + \
@@ -30,6 +32,7 @@ struct mlxsw_hwmon {
        struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1];
        struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT];
        unsigned int attrs_count;
+       u8 sensor_count;
 };
 
 static ssize_t mlxsw_hwmon_temp_show(struct device *dev,
@@ -121,6 +124,27 @@ static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev,
        return sprintf(buf, "%u\n", mlxsw_reg_mfsm_rpm_get(mfsm_pl));
 }
 
+static ssize_t mlxsw_hwmon_fan_fault_show(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+                       container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+       struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+       char fore_pl[MLXSW_REG_FORE_LEN];
+       bool fault;
+       int err;
+
+       err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(fore), fore_pl);
+       if (err) {
+               dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n");
+               return err;
+       }
+       mlxsw_reg_fore_unpack(fore_pl, mlwsw_hwmon_attr->type_index, &fault);
+
+       return sprintf(buf, "%u\n", fault);
+}
+
 static ssize_t mlxsw_hwmon_pwm_show(struct device *dev,
                                    struct device_attribute *attr,
                                    char *buf)
@@ -167,12 +191,160 @@ static ssize_t mlxsw_hwmon_pwm_store(struct device *dev,
        return len;
 }
 
+static ssize_t mlxsw_hwmon_module_temp_show(struct device *dev,
+                                           struct device_attribute *attr,
+                                           char *buf)
+{
+       struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+                       container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+       struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+       char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};
+       u16 temp;
+       u8 module;
+       int err;
+
+       module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
+       mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
+                           1);
+       err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtbr), mtbr_pl);
+       if (err) {
+               dev_err(dev, "Failed to query module temperature sensor\n");
+               return err;
+       }
+
+       mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
+       /* Update status and temperature cache. */
+       switch (temp) {
+       case MLXSW_REG_MTBR_NO_CONN: /* fall-through */
+       case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */
+       case MLXSW_REG_MTBR_INDEX_NA:
+               temp = 0;
+               break;
+       case MLXSW_REG_MTBR_BAD_SENS_INFO:
+               /* Untrusted cable is connected. Reading temperature from its
+                * sensor is faulty.
+                */
+               temp = 0;
+               break;
+       default:
+               temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp);
+               break;
+       }
+
+       return sprintf(buf, "%u\n", temp);
+}
+
+static ssize_t mlxsw_hwmon_module_temp_fault_show(struct device *dev,
+                                                 struct device_attribute *attr,
+                                                 char *buf)
+{
+       struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+                       container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+       struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+       char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};
+       u8 module, fault;
+       u16 temp;
+       int err;
+
+       module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
+       mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
+                           1);
+       err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtbr), mtbr_pl);
+       if (err) {
+               dev_err(dev, "Failed to query module temperature sensor\n");
+               return err;
+       }
+
+       mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
+
+       /* Update status and temperature cache. */
+       switch (temp) {
+       case MLXSW_REG_MTBR_BAD_SENS_INFO:
+               /* Untrusted cable is connected. Reading temperature from its
+                * sensor is faulty.
+                */
+               fault = 1;
+               break;
+       case MLXSW_REG_MTBR_NO_CONN: /* fall-through */
+       case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */
+       case MLXSW_REG_MTBR_INDEX_NA:
+       default:
+               fault = 0;
+               break;
+       }
+
+       return sprintf(buf, "%u\n", fault);
+}
+
+static ssize_t
+mlxsw_hwmon_module_temp_critical_show(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+                       container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+       struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+       int temp;
+       u8 module;
+       int err;
+
+       module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
+       err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core, module,
+                                                  SFP_TEMP_HIGH_WARN, &temp);
+       if (err) {
+               dev_err(dev, "Failed to query module temperature thresholds\n");
+               return err;
+       }
+
+       return sprintf(buf, "%u\n", temp);
+}
+
+static ssize_t
+mlxsw_hwmon_module_temp_emergency_show(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+                       container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+       struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+       u8 module;
+       int temp;
+       int err;
+
+       module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
+       err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core, module,
+                                                  SFP_TEMP_HIGH_ALARM, &temp);
+       if (err) {
+               dev_err(dev, "Failed to query module temperature thresholds\n");
+               return err;
+       }
+
+       return sprintf(buf, "%u\n", temp);
+}
+
+static ssize_t
+mlxsw_hwmon_module_temp_label_show(struct device *dev,
+                                  struct device_attribute *attr,
+                                  char *buf)
+{
+       struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+                       container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+
+       return sprintf(buf, "front panel %03u\n",
+                      mlwsw_hwmon_attr->type_index);
+}
+
 enum mlxsw_hwmon_attr_type {
        MLXSW_HWMON_ATTR_TYPE_TEMP,
        MLXSW_HWMON_ATTR_TYPE_TEMP_MAX,
        MLXSW_HWMON_ATTR_TYPE_TEMP_RST,
        MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
+       MLXSW_HWMON_ATTR_TYPE_FAN_FAULT,
        MLXSW_HWMON_ATTR_TYPE_PWM,
+       MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE,
+       MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT,
+       MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT,
+       MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG,
+       MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL,
 };
 
 static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
@@ -209,6 +381,12 @@ static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
                snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
                         "fan%u_input", num + 1);
                break;
+       case MLXSW_HWMON_ATTR_TYPE_FAN_FAULT:
+               mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_fault_show;
+               mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
+               snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+                        "fan%u_fault", num + 1);
+               break;
        case MLXSW_HWMON_ATTR_TYPE_PWM:
                mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show;
                mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store;
@@ -216,6 +394,40 @@ static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
                snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
                         "pwm%u", num + 1);
                break;
+       case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE:
+               mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_module_temp_show;
+               mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
+               snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+                        "temp%u_input", num + 1);
+               break;
+       case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT:
+               mlxsw_hwmon_attr->dev_attr.show =
+                                       mlxsw_hwmon_module_temp_fault_show;
+               mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
+               snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+                        "temp%u_fault", num + 1);
+               break;
+       case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT:
+               mlxsw_hwmon_attr->dev_attr.show =
+                       mlxsw_hwmon_module_temp_critical_show;
+               mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
+               snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+                        "temp%u_crit", num + 1);
+               break;
+       case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG:
+               mlxsw_hwmon_attr->dev_attr.show =
+                       mlxsw_hwmon_module_temp_emergency_show;
+               mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
+               snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+                        "temp%u_emergency", num + 1);
+               break;
+       case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL:
+               mlxsw_hwmon_attr->dev_attr.show =
+                       mlxsw_hwmon_module_temp_label_show;
+               mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
+               snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+                        "temp%u_label", num + 1);
+               break;
        default:
                WARN_ON(1);
        }
@@ -233,7 +445,6 @@ static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon)
 {
        char mtcap_pl[MLXSW_REG_MTCAP_LEN] = {0};
        char mtmp_pl[MLXSW_REG_MTMP_LEN];
-       u8 sensor_count;
        int i;
        int err;
 
@@ -242,8 +453,8 @@ static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon)
                dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n");
                return err;
        }
-       sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);
-       for (i = 0; i < sensor_count; i++) {
+       mlxsw_hwmon->sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);
+       for (i = 0; i < mlxsw_hwmon->sensor_count; i++) {
                mlxsw_reg_mtmp_pack(mtmp_pl, i, true, true);
                err = mlxsw_reg_write(mlxsw_hwmon->core,
                                      MLXSW_REG(mtmp), mtmp_pl);
@@ -280,10 +491,14 @@ static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon)
        mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active);
        num = 0;
        for (type_index = 0; type_index < MLXSW_MFCR_TACHOS_MAX; type_index++) {
-               if (tacho_active & BIT(type_index))
+               if (tacho_active & BIT(type_index)) {
                        mlxsw_hwmon_attr_add(mlxsw_hwmon,
                                             MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
+                                            type_index, num);
+                       mlxsw_hwmon_attr_add(mlxsw_hwmon,
+                                            MLXSW_HWMON_ATTR_TYPE_FAN_FAULT,
                                             type_index, num++);
+               }
        }
        num = 0;
        for (type_index = 0; type_index < MLXSW_MFCR_PWMS_MAX; type_index++) {
@@ -295,6 +510,53 @@ static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon)
        return 0;
 }
 
+static int mlxsw_hwmon_module_init(struct mlxsw_hwmon *mlxsw_hwmon)
+{
+       unsigned int module_count = mlxsw_core_max_ports(mlxsw_hwmon->core);
+       char pmlp_pl[MLXSW_REG_PMLP_LEN] = {0};
+       int i, index;
+       u8 width;
+       int err;
+
+       /* Add extra attributes for module temperature. Sensor index is
+        * assigned to sensor_count value, while all indexed before
+        * sensor_count are already utilized by the sensors connected through
+        * mtmp register by mlxsw_hwmon_temp_init().
+        */
+       index = mlxsw_hwmon->sensor_count;
+       for (i = 1; i < module_count; i++) {
+               mlxsw_reg_pmlp_pack(pmlp_pl, i);
+               err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(pmlp),
+                                     pmlp_pl);
+               if (err) {
+                       dev_err(mlxsw_hwmon->bus_info->dev, "Failed to read module index %d\n",
+                               i);
+                       return err;
+               }
+               width = mlxsw_reg_pmlp_width_get(pmlp_pl);
+               if (!width)
+                       continue;
+               mlxsw_hwmon_attr_add(mlxsw_hwmon,
+                                    MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE, index,
+                                    index);
+               mlxsw_hwmon_attr_add(mlxsw_hwmon,
+                                    MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT,
+                                    index, index);
+               mlxsw_hwmon_attr_add(mlxsw_hwmon,
+                                    MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT,
+                                    index, index);
+               mlxsw_hwmon_attr_add(mlxsw_hwmon,
+                                    MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG,
+                                    index, index);
+               mlxsw_hwmon_attr_add(mlxsw_hwmon,
+                                    MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL,
+                                    index, index);
+               index++;
+       }
+
+       return 0;
+}
+
 int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
                     const struct mlxsw_bus_info *mlxsw_bus_info,
                     struct mlxsw_hwmon **p_hwmon)
@@ -317,6 +579,10 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
        if (err)
                goto err_fans_init;
 
+       err = mlxsw_hwmon_module_init(mlxsw_hwmon);
+       if (err)
+               goto err_temp_module_init;
+
        mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group;
        mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs;
 
@@ -333,6 +599,7 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
        return 0;
 
 err_hwmon_register:
+err_temp_module_init:
 err_fans_init:
 err_temp_init:
        kfree(mlxsw_hwmon);
index 61f897b..0b85c72 100644 (file)
@@ -9,11 +9,20 @@
 #include <linux/sysfs.h>
 #include <linux/thermal.h>
 #include <linux/err.h>
+#include <linux/sfp.h>
 
 #include "core.h"
+#include "core_env.h"
 
 #define MLXSW_THERMAL_POLL_INT 1000    /* ms */
-#define MLXSW_THERMAL_MAX_TEMP 110000  /* 110C */
+#define MLXSW_THERMAL_SLOW_POLL_INT    20000   /* ms */
+#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_HYSTERESIS_TEMP  5000    /* 5C */
+#define MLXSW_THERMAL_MODULE_TEMP_SHIFT        (MLXSW_THERMAL_HYSTERESIS_TEMP * 2)
+#define MLXSW_THERMAL_ZONE_MAX_NAME    16
 #define MLXSW_THERMAL_MAX_STATE        10
 #define MLXSW_THERMAL_MAX_DUTY 255
 /* Minimum and maximum fan allowed speed in percent: from 20% to 100%. Values
 #define MLXSW_THERMAL_SPEED_MAX                (MLXSW_THERMAL_MAX_STATE * 2)
 #define MLXSW_THERMAL_SPEED_MIN_LEVEL  2               /* 20% */
 
+/* External cooling devices, allowed for binding to mlxsw thermal zones. */
+static char * const mlxsw_thermal_external_allowed_cdev[] = {
+       "mlxreg_fan",
+};
+
+enum mlxsw_thermal_trips {
+       MLXSW_THERMAL_TEMP_TRIP_NORM,
+       MLXSW_THERMAL_TEMP_TRIP_HIGH,
+       MLXSW_THERMAL_TEMP_TRIP_HOT,
+       MLXSW_THERMAL_TEMP_TRIP_CRIT,
+};
+
 struct mlxsw_thermal_trip {
        int     type;
        int     temp;
+       int     hyst;
        int     min_state;
        int     max_state;
 };
@@ -36,32 +58,29 @@ struct mlxsw_thermal_trip {
 static const struct mlxsw_thermal_trip default_thermal_trips[] = {
        {       /* In range - 0-40% PWM */
                .type           = THERMAL_TRIP_ACTIVE,
-               .temp           = 75000,
+               .temp           = MLXSW_THERMAL_ASIC_TEMP_NORM,
+               .hyst           = MLXSW_THERMAL_HYSTERESIS_TEMP,
                .min_state      = 0,
                .max_state      = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
        },
-       {       /* High - 40-100% PWM */
-               .type           = THERMAL_TRIP_ACTIVE,
-               .temp           = 80000,
-               .min_state      = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
-               .max_state      = MLXSW_THERMAL_MAX_STATE,
-       },
        {
-               /* Very high - 100% PWM */
+               /* In range - 40-100% PWM */
                .type           = THERMAL_TRIP_ACTIVE,
-               .temp           = 85000,
-               .min_state      = MLXSW_THERMAL_MAX_STATE,
+               .temp           = MLXSW_THERMAL_ASIC_TEMP_HIGH,
+               .hyst           = MLXSW_THERMAL_HYSTERESIS_TEMP,
+               .min_state      = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
                .max_state      = MLXSW_THERMAL_MAX_STATE,
        },
        {       /* Warning */
                .type           = THERMAL_TRIP_HOT,
-               .temp           = 105000,
+               .temp           = MLXSW_THERMAL_ASIC_TEMP_HOT,
+               .hyst           = MLXSW_THERMAL_HYSTERESIS_TEMP,
                .min_state      = MLXSW_THERMAL_MAX_STATE,
                .max_state      = MLXSW_THERMAL_MAX_STATE,
        },
        {       /* Critical - soft poweroff */
                .type           = THERMAL_TRIP_CRITICAL,
-               .temp           = MLXSW_THERMAL_MAX_TEMP,
+               .temp           = MLXSW_THERMAL_ASIC_TEMP_CRIT,
                .min_state      = MLXSW_THERMAL_MAX_STATE,
                .max_state      = MLXSW_THERMAL_MAX_STATE,
        }
@@ -72,14 +91,27 @@ static const struct mlxsw_thermal_trip default_thermal_trips[] = {
 /* Make sure all trips are writable */
 #define MLXSW_THERMAL_TRIP_MASK        (BIT(MLXSW_THERMAL_NUM_TRIPS) - 1)
 
+struct mlxsw_thermal;
+
+struct mlxsw_thermal_module {
+       struct mlxsw_thermal *parent;
+       struct thermal_zone_device *tzdev;
+       struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
+       enum thermal_device_mode mode;
+       int module;
+};
+
 struct mlxsw_thermal {
        struct mlxsw_core *core;
        const struct mlxsw_bus_info *bus_info;
        struct thermal_zone_device *tzdev;
+       int polling_delay;
        struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX];
        u8 cooling_levels[MLXSW_THERMAL_MAX_STATE + 1];
        struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
        enum thermal_device_mode mode;
+       struct mlxsw_thermal_module *tz_module_arr;
+       unsigned int tz_module_num;
 };
 
 static inline u8 mlxsw_state_to_duty(int state)
@@ -103,9 +135,67 @@ static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal,
                if (thermal->cdevs[i] == cdev)
                        return i;
 
+       /* Allow mlxsw thermal zone binding to an external cooling device */
+       for (i = 0; i < ARRAY_SIZE(mlxsw_thermal_external_allowed_cdev); i++) {
+               if (strnstr(cdev->type, mlxsw_thermal_external_allowed_cdev[i],
+                           sizeof(cdev->type)))
+                       return 0;
+       }
+
        return -ENODEV;
 }
 
+static void
+mlxsw_thermal_module_trips_reset(struct mlxsw_thermal_module *tz)
+{
+       tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temp = 0;
+       tz->trips[MLXSW_THERMAL_TEMP_TRIP_HIGH].temp = 0;
+       tz->trips[MLXSW_THERMAL_TEMP_TRIP_HOT].temp = 0;
+       tz->trips[MLXSW_THERMAL_TEMP_TRIP_CRIT].temp = 0;
+}
+
+static int
+mlxsw_thermal_module_trips_update(struct device *dev, struct mlxsw_core *core,
+                                 struct mlxsw_thermal_module *tz)
+{
+       int crit_temp, emerg_temp;
+       int err;
+
+       err = mlxsw_env_module_temp_thresholds_get(core, tz->module,
+                                                  SFP_TEMP_HIGH_WARN,
+                                                  &crit_temp);
+       if (err)
+               return err;
+
+       err = mlxsw_env_module_temp_thresholds_get(core, tz->module,
+                                                  SFP_TEMP_HIGH_ALARM,
+                                                  &emerg_temp);
+       if (err)
+               return err;
+
+       /* 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"
+        * and "hot" trip points, "normal" and "critical" trip points are
+        * derived from "active" and "hot" by subtracting or adding double
+        * hysteresis value.
+        */
+       if (crit_temp >= MLXSW_THERMAL_MODULE_TEMP_SHIFT)
+               tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temp = crit_temp -
+                                       MLXSW_THERMAL_MODULE_TEMP_SHIFT;
+       else
+               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 +
+                                       MLXSW_THERMAL_MODULE_TEMP_SHIFT;
+       else
+               tz->trips[MLXSW_THERMAL_TEMP_TRIP_CRIT].temp = emerg_temp;
+
+       return 0;
+}
+
 static int mlxsw_thermal_bind(struct thermal_zone_device *tzdev,
                              struct thermal_cooling_device *cdev)
 {
@@ -172,7 +262,7 @@ static int mlxsw_thermal_set_mode(struct thermal_zone_device *tzdev,
        mutex_lock(&tzdev->lock);
 
        if (mode == THERMAL_DEVICE_ENABLED)
-               tzdev->polling_delay = MLXSW_THERMAL_POLL_INT;
+               tzdev->polling_delay = thermal->polling_delay;
        else
                tzdev->polling_delay = 0;
 
@@ -237,13 +327,31 @@ static int mlxsw_thermal_set_trip_temp(struct thermal_zone_device *tzdev,
        struct mlxsw_thermal *thermal = tzdev->devdata;
 
        if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS ||
-           temp > MLXSW_THERMAL_MAX_TEMP)
+           temp > MLXSW_THERMAL_ASIC_TEMP_CRIT)
                return -EINVAL;
 
        thermal->trips[trip].temp = temp;
        return 0;
 }
 
+static int mlxsw_thermal_get_trip_hyst(struct thermal_zone_device *tzdev,
+                                      int trip, int *p_hyst)
+{
+       struct mlxsw_thermal *thermal = tzdev->devdata;
+
+       *p_hyst = thermal->trips[trip].hyst;
+       return 0;
+}
+
+static int mlxsw_thermal_set_trip_hyst(struct thermal_zone_device *tzdev,
+                                      int trip, int hyst)
+{
+       struct mlxsw_thermal *thermal = tzdev->devdata;
+
+       thermal->trips[trip].hyst = hyst;
+       return 0;
+}
+
 static struct thermal_zone_device_ops mlxsw_thermal_ops = {
        .bind = mlxsw_thermal_bind,
        .unbind = mlxsw_thermal_unbind,
@@ -253,6 +361,206 @@ static struct thermal_zone_device_ops mlxsw_thermal_ops = {
        .get_trip_type  = mlxsw_thermal_get_trip_type,
        .get_trip_temp  = mlxsw_thermal_get_trip_temp,
        .set_trip_temp  = mlxsw_thermal_set_trip_temp,
+       .get_trip_hyst  = mlxsw_thermal_get_trip_hyst,
+       .set_trip_hyst  = mlxsw_thermal_set_trip_hyst,
+};
+
+static int mlxsw_thermal_module_bind(struct thermal_zone_device *tzdev,
+                                    struct thermal_cooling_device *cdev)
+{
+       struct mlxsw_thermal_module *tz = tzdev->devdata;
+       struct mlxsw_thermal *thermal = tz->parent;
+       int i, j, err;
+
+       /* If the cooling device is one of ours bind it */
+       if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0)
+               return 0;
+
+       for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) {
+               const struct mlxsw_thermal_trip *trip = &tz->trips[i];
+
+               err = thermal_zone_bind_cooling_device(tzdev, i, cdev,
+                                                      trip->max_state,
+                                                      trip->min_state,
+                                                      THERMAL_WEIGHT_DEFAULT);
+               if (err < 0)
+                       goto err_bind_cooling_device;
+       }
+       return 0;
+
+err_bind_cooling_device:
+       for (j = i - 1; j >= 0; j--)
+               thermal_zone_unbind_cooling_device(tzdev, j, cdev);
+       return err;
+}
+
+static int mlxsw_thermal_module_unbind(struct thermal_zone_device *tzdev,
+                                      struct thermal_cooling_device *cdev)
+{
+       struct mlxsw_thermal_module *tz = tzdev->devdata;
+       struct mlxsw_thermal *thermal = tz->parent;
+       int i;
+       int err;
+
+       /* If the cooling device is one of ours unbind it */
+       if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0)
+               return 0;
+
+       for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) {
+               err = thermal_zone_unbind_cooling_device(tzdev, i, cdev);
+               WARN_ON(err);
+       }
+       return err;
+}
+
+static int mlxsw_thermal_module_mode_get(struct thermal_zone_device *tzdev,
+                                        enum thermal_device_mode *mode)
+{
+       struct mlxsw_thermal_module *tz = tzdev->devdata;
+
+       *mode = tz->mode;
+
+       return 0;
+}
+
+static int mlxsw_thermal_module_mode_set(struct thermal_zone_device *tzdev,
+                                        enum thermal_device_mode mode)
+{
+       struct mlxsw_thermal_module *tz = tzdev->devdata;
+       struct mlxsw_thermal *thermal = tz->parent;
+
+       mutex_lock(&tzdev->lock);
+
+       if (mode == THERMAL_DEVICE_ENABLED)
+               tzdev->polling_delay = thermal->polling_delay;
+       else
+               tzdev->polling_delay = 0;
+
+       mutex_unlock(&tzdev->lock);
+
+       tz->mode = mode;
+       thermal_zone_device_update(tzdev, THERMAL_EVENT_UNSPECIFIED);
+
+       return 0;
+}
+
+static int mlxsw_thermal_module_temp_get(struct thermal_zone_device *tzdev,
+                                        int *p_temp)
+{
+       struct mlxsw_thermal_module *tz = tzdev->devdata;
+       struct mlxsw_thermal *thermal = tz->parent;
+       struct device *dev = thermal->bus_info->dev;
+       char mtbr_pl[MLXSW_REG_MTBR_LEN];
+       u16 temp;
+       int err;
+
+       /* Read module temperature. */
+       mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX +
+                           tz->module, 1);
+       err = mlxsw_reg_query(thermal->core, MLXSW_REG(mtbr), mtbr_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
+       /* Update temperature. */
+       switch (temp) {
+       case MLXSW_REG_MTBR_NO_CONN: /* fall-through */
+       case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */
+       case MLXSW_REG_MTBR_INDEX_NA: /* fall-through */
+       case MLXSW_REG_MTBR_BAD_SENS_INFO:
+               temp = 0;
+               break;
+       default:
+               temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp);
+               /* Reset all trip point. */
+               mlxsw_thermal_module_trips_reset(tz);
+               /* Update trip points. */
+               err = mlxsw_thermal_module_trips_update(dev, thermal->core,
+                                                       tz);
+               if (err)
+                       return err;
+               break;
+       }
+
+       *p_temp = (int) temp;
+       return 0;
+}
+
+static int
+mlxsw_thermal_module_trip_type_get(struct thermal_zone_device *tzdev, int trip,
+                                  enum thermal_trip_type *p_type)
+{
+       struct mlxsw_thermal_module *tz = tzdev->devdata;
+
+       if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS)
+               return -EINVAL;
+
+       *p_type = tz->trips[trip].type;
+       return 0;
+}
+
+static int
+mlxsw_thermal_module_trip_temp_get(struct thermal_zone_device *tzdev,
+                                  int trip, int *p_temp)
+{
+       struct mlxsw_thermal_module *tz = tzdev->devdata;
+
+       if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS)
+               return -EINVAL;
+
+       *p_temp = tz->trips[trip].temp;
+       return 0;
+}
+
+static int
+mlxsw_thermal_module_trip_temp_set(struct thermal_zone_device *tzdev,
+                                  int trip, int temp)
+{
+       struct mlxsw_thermal_module *tz = tzdev->devdata;
+
+       if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS ||
+           temp > tz->trips[MLXSW_THERMAL_TEMP_TRIP_CRIT].temp)
+               return -EINVAL;
+
+       tz->trips[trip].temp = temp;
+       return 0;
+}
+
+static int
+mlxsw_thermal_module_trip_hyst_get(struct thermal_zone_device *tzdev, int trip,
+                                  int *p_hyst)
+{
+       struct mlxsw_thermal_module *tz = tzdev->devdata;
+
+       *p_hyst = tz->trips[trip].hyst;
+       return 0;
+}
+
+static int
+mlxsw_thermal_module_trip_hyst_set(struct thermal_zone_device *tzdev, int trip,
+                                  int hyst)
+{
+       struct mlxsw_thermal_module *tz = tzdev->devdata;
+
+       tz->trips[trip].hyst = hyst;
+       return 0;
+}
+
+static struct thermal_zone_params mlxsw_thermal_module_params = {
+       .governor_name = "user_space",
+};
+
+static struct thermal_zone_device_ops mlxsw_thermal_module_ops = {
+       .bind           = mlxsw_thermal_module_bind,
+       .unbind         = mlxsw_thermal_module_unbind,
+       .get_mode       = mlxsw_thermal_module_mode_get,
+       .set_mode       = mlxsw_thermal_module_mode_set,
+       .get_temp       = mlxsw_thermal_module_temp_get,
+       .get_trip_type  = mlxsw_thermal_module_trip_type_get,
+       .get_trip_temp  = mlxsw_thermal_module_trip_temp_get,
+       .set_trip_temp  = mlxsw_thermal_module_trip_temp_set,
+       .get_trip_hyst  = mlxsw_thermal_module_trip_hyst_get,
+       .set_trip_hyst  = mlxsw_thermal_module_trip_hyst_set,
 };
 
 static int mlxsw_thermal_get_max_state(struct thermal_cooling_device *cdev,
@@ -355,6 +663,123 @@ static const struct thermal_cooling_device_ops mlxsw_cooling_ops = {
        .set_cur_state  = mlxsw_thermal_set_cur_state,
 };
 
+static int
+mlxsw_thermal_module_tz_init(struct mlxsw_thermal_module *module_tz)
+{
+       char tz_name[MLXSW_THERMAL_ZONE_MAX_NAME];
+       int err;
+
+       snprintf(tz_name, sizeof(tz_name), "mlxsw-module%d",
+                module_tz->module + 1);
+       module_tz->tzdev = thermal_zone_device_register(tz_name,
+                                                       MLXSW_THERMAL_NUM_TRIPS,
+                                                       MLXSW_THERMAL_TRIP_MASK,
+                                                       module_tz,
+                                                       &mlxsw_thermal_module_ops,
+                                                       &mlxsw_thermal_module_params,
+                                                       0, 0);
+       if (IS_ERR(module_tz->tzdev)) {
+               err = PTR_ERR(module_tz->tzdev);
+               return err;
+       }
+
+       return 0;
+}
+
+static void mlxsw_thermal_module_tz_fini(struct thermal_zone_device *tzdev)
+{
+       thermal_zone_device_unregister(tzdev);
+}
+
+static int
+mlxsw_thermal_module_init(struct device *dev, struct mlxsw_core *core,
+                         struct mlxsw_thermal *thermal, u8 local_port)
+{
+       struct mlxsw_thermal_module *module_tz;
+       char pmlp_pl[MLXSW_REG_PMLP_LEN];
+       u8 width, module;
+       int err;
+
+       mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
+       err = mlxsw_reg_query(core, MLXSW_REG(pmlp), pmlp_pl);
+       if (err)
+               return err;
+
+       width = mlxsw_reg_pmlp_width_get(pmlp_pl);
+       if (!width)
+               return 0;
+
+       module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
+       module_tz = &thermal->tz_module_arr[module];
+       module_tz->module = module;
+       module_tz->parent = thermal;
+       memcpy(module_tz->trips, default_thermal_trips,
+              sizeof(thermal->trips));
+       /* Initialize all trip point. */
+       mlxsw_thermal_module_trips_reset(module_tz);
+       /* Update trip point according to the module data. */
+       err = mlxsw_thermal_module_trips_update(dev, core, module_tz);
+       if (err)
+               return err;
+
+       thermal->tz_module_num++;
+
+       return 0;
+}
+
+static void mlxsw_thermal_module_fini(struct mlxsw_thermal_module *module_tz)
+{
+       if (module_tz && module_tz->tzdev) {
+               mlxsw_thermal_module_tz_fini(module_tz->tzdev);
+               module_tz->tzdev = NULL;
+       }
+}
+
+static int
+mlxsw_thermal_modules_init(struct device *dev, struct mlxsw_core *core,
+                          struct mlxsw_thermal *thermal)
+{
+       unsigned int module_count = mlxsw_core_max_ports(core);
+       int i, err;
+
+       thermal->tz_module_arr = kcalloc(module_count,
+                                        sizeof(*thermal->tz_module_arr),
+                                        GFP_KERNEL);
+       if (!thermal->tz_module_arr)
+               return -ENOMEM;
+
+       for (i = 1; i < module_count; i++) {
+               err = mlxsw_thermal_module_init(dev, core, thermal, i);
+               if (err)
+                       goto err_unreg_tz_module_arr;
+       }
+
+       for (i = 0; i < thermal->tz_module_num; i++) {
+               err = mlxsw_thermal_module_tz_init(&thermal->tz_module_arr[i]);
+               if (err)
+                       goto err_unreg_tz_module_arr;
+       }
+
+       return 0;
+
+err_unreg_tz_module_arr:
+       for (i = module_count - 1; i >= 0; i--)
+               mlxsw_thermal_module_fini(&thermal->tz_module_arr[i]);
+       kfree(thermal->tz_module_arr);
+       return err;
+}
+
+static void
+mlxsw_thermal_modules_fini(struct mlxsw_thermal *thermal)
+{
+       unsigned int module_count = mlxsw_core_max_ports(thermal->core);
+       int i;
+
+       for (i = module_count - 1; i >= 0; i--)
+               mlxsw_thermal_module_fini(&thermal->tz_module_arr[i]);
+       kfree(thermal->tz_module_arr);
+}
+
 int mlxsw_thermal_init(struct mlxsw_core *core,
                       const struct mlxsw_bus_info *bus_info,
                       struct mlxsw_thermal **p_thermal)
@@ -407,8 +832,9 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
                if (pwm_active & BIT(i)) {
                        struct thermal_cooling_device *cdev;
 
-                       cdev = thermal_cooling_device_register("Fan", thermal,
-                                                       &mlxsw_cooling_ops);
+                       cdev = thermal_cooling_device_register("mlxsw_fan",
+                                                              thermal,
+                                                              &mlxsw_cooling_ops);
                        if (IS_ERR(cdev)) {
                                err = PTR_ERR(cdev);
                                dev_err(dev, "Failed to register cooling device\n");
@@ -423,22 +849,36 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
                thermal->cooling_levels[i] = max(MLXSW_THERMAL_SPEED_MIN_LEVEL,
                                                 i);
 
+       thermal->polling_delay = bus_info->low_frequency ?
+                                MLXSW_THERMAL_SLOW_POLL_INT :
+                                MLXSW_THERMAL_POLL_INT;
+
        thermal->tzdev = thermal_zone_device_register("mlxsw",
                                                      MLXSW_THERMAL_NUM_TRIPS,
                                                      MLXSW_THERMAL_TRIP_MASK,
                                                      thermal,
                                                      &mlxsw_thermal_ops,
                                                      NULL, 0,
-                                                     MLXSW_THERMAL_POLL_INT);
+                                                     thermal->polling_delay);
        if (IS_ERR(thermal->tzdev)) {
                err = PTR_ERR(thermal->tzdev);
                dev_err(dev, "Failed to register thermal zone\n");
                goto err_unreg_cdevs;
        }
 
+       err = mlxsw_thermal_modules_init(dev, core, thermal);
+       if (err)
+               goto err_unreg_tzdev;
+
        thermal->mode = THERMAL_DEVICE_ENABLED;
        *p_thermal = thermal;
        return 0;
+
+err_unreg_tzdev:
+       if (thermal->tzdev) {
+               thermal_zone_device_unregister(thermal->tzdev);
+               thermal->tzdev = NULL;
+       }
 err_unreg_cdevs:
        for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++)
                if (thermal->cdevs[i])
@@ -452,6 +892,7 @@ void mlxsw_thermal_fini(struct mlxsw_thermal *thermal)
 {
        int i;
 
+       mlxsw_thermal_modules_fini(thermal);
        if (thermal->tzdev) {
                thermal_zone_device_unregister(thermal->tzdev);
                thermal->tzdev = NULL;
index 798bd5a..a87ca6b 100644 (file)
@@ -503,6 +503,7 @@ static int mlxsw_i2c_probe(struct i2c_client *client,
        mlxsw_i2c->bus_info.device_kind = id->name;
        mlxsw_i2c->bus_info.device_name = client->name;
        mlxsw_i2c->bus_info.dev = &client->dev;
+       mlxsw_i2c->bus_info.low_frequency = true;
        mlxsw_i2c->dev = &client->dev;
 
        err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info,
index 5f8066a..cbd0193 100644 (file)
@@ -2199,6 +2199,14 @@ MLXSW_ITEM32(reg, pagt, size, 0x00, 0, 8);
  */
 MLXSW_ITEM32(reg, pagt, acl_group_id, 0x08, 0, 16);
 
+/* reg_pagt_multi
+ * Multi-ACL
+ * 0 - This ACL is the last ACL in the multi-ACL
+ * 1 - This ACL is part of a multi-ACL
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, pagt, multi, 0x30, 31, 1, 0x04, 0x00, false);
+
 /* reg_pagt_acl_id
  * ACL identifier
  * Access: RW
@@ -2212,12 +2220,13 @@ static inline void mlxsw_reg_pagt_pack(char *payload, u16 acl_group_id)
 }
 
 static inline void mlxsw_reg_pagt_acl_id_pack(char *payload, int index,
-                                             u16 acl_id)
+                                             u16 acl_id, bool multi)
 {
        u8 size = mlxsw_reg_pagt_size_get(payload);
 
        if (index >= size)
                mlxsw_reg_pagt_size_set(payload, index + 1);
+       mlxsw_reg_pagt_multi_set(payload, index, multi);
        mlxsw_reg_pagt_acl_id_set(payload, index, acl_id);
 }
 
@@ -7866,6 +7875,35 @@ static inline void mlxsw_reg_mfsl_unpack(char *payload, u8 tacho,
                *p_tach_max = mlxsw_reg_mfsl_tach_max_get(payload);
 }
 
+/* FORE - Fan Out of Range Event Register
+ * --------------------------------------
+ * This register reports the status of the controlled fans compared to the
+ * range defined by the MFSL register.
+ */
+#define MLXSW_REG_FORE_ID 0x9007
+#define MLXSW_REG_FORE_LEN 0x0C
+
+MLXSW_REG_DEFINE(fore, MLXSW_REG_FORE_ID, MLXSW_REG_FORE_LEN);
+
+/* fan_under_limit
+ * Fan speed is below the low limit defined in MFSL register. Each bit relates
+ * to a single tachometer and indicates the specific tachometer reading is
+ * below the threshold.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, fore, fan_under_limit, 0x00, 16, 10);
+
+static inline void mlxsw_reg_fore_unpack(char *payload, u8 tacho,
+                                        bool *fault)
+{
+       u16 limit;
+
+       if (fault) {
+               limit = mlxsw_reg_fore_fan_under_limit_get(payload);
+               *fault = limit & BIT(tacho);
+       }
+}
+
 /* MTCAP - Management Temperature Capabilities
  * -------------------------------------------
  * This register exposes the capabilities of the device and
@@ -7992,6 +8030,80 @@ static inline void mlxsw_reg_mtmp_unpack(char *payload, unsigned int *p_temp,
                mlxsw_reg_mtmp_sensor_name_memcpy_from(payload, sensor_name);
 }
 
+/* MTBR - Management Temperature Bulk Register
+ * -------------------------------------------
+ * This register is used for bulk temperature reading.
+ */
+#define MLXSW_REG_MTBR_ID 0x900F
+#define MLXSW_REG_MTBR_BASE_LEN 0x10 /* base length, without records */
+#define MLXSW_REG_MTBR_REC_LEN 0x04 /* record length */
+#define MLXSW_REG_MTBR_REC_MAX_COUNT 47 /* firmware limitation */
+#define MLXSW_REG_MTBR_LEN (MLXSW_REG_MTBR_BASE_LEN +  \
+                           MLXSW_REG_MTBR_REC_LEN *    \
+                           MLXSW_REG_MTBR_REC_MAX_COUNT)
+
+MLXSW_REG_DEFINE(mtbr, MLXSW_REG_MTBR_ID, MLXSW_REG_MTBR_LEN);
+
+/* reg_mtbr_base_sensor_index
+ * Base sensors index to access (0 - ASIC sensor, 1-63 - ambient sensors,
+ * 64-127 are mapped to the SFP+/QSFP modules sequentially).
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, mtbr, base_sensor_index, 0x00, 0, 7);
+
+/* reg_mtbr_num_rec
+ * Request: Number of records to read
+ * Response: Number of records read
+ * See above description for more details.
+ * Range 1..255
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mtbr, num_rec, 0x04, 0, 8);
+
+/* reg_mtbr_rec_max_temp
+ * The highest measured temperature from the sensor.
+ * When the bit mte is cleared, the field max_temperature is reserved.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, mtbr, rec_max_temp, MLXSW_REG_MTBR_BASE_LEN, 16,
+                    16, MLXSW_REG_MTBR_REC_LEN, 0x00, false);
+
+/* reg_mtbr_rec_temp
+ * Temperature reading from the sensor. Reading is in 0..125 Celsius
+ * degrees units.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, mtbr, rec_temp, MLXSW_REG_MTBR_BASE_LEN, 0, 16,
+                    MLXSW_REG_MTBR_REC_LEN, 0x00, false);
+
+static inline void mlxsw_reg_mtbr_pack(char *payload, u8 base_sensor_index,
+                                      u8 num_rec)
+{
+       MLXSW_REG_ZERO(mtbr, payload);
+       mlxsw_reg_mtbr_base_sensor_index_set(payload, base_sensor_index);
+       mlxsw_reg_mtbr_num_rec_set(payload, num_rec);
+}
+
+/* Error codes from temperatute reading */
+enum mlxsw_reg_mtbr_temp_status {
+       MLXSW_REG_MTBR_NO_CONN          = 0x8000,
+       MLXSW_REG_MTBR_NO_TEMP_SENS     = 0x8001,
+       MLXSW_REG_MTBR_INDEX_NA         = 0x8002,
+       MLXSW_REG_MTBR_BAD_SENS_INFO    = 0x8003,
+};
+
+/* Base index for reading modules temperature */
+#define MLXSW_REG_MTBR_BASE_MODULE_INDEX 64
+
+static inline void mlxsw_reg_mtbr_temp_unpack(char *payload, int rec_ind,
+                                             u16 *p_temp, u16 *p_max_temp)
+{
+       if (p_temp)
+               *p_temp = mlxsw_reg_mtbr_rec_temp_get(payload, rec_ind);
+       if (p_max_temp)
+               *p_max_temp = mlxsw_reg_mtbr_rec_max_temp_get(payload, rec_ind);
+}
+
 /* MCIA - Management Cable Info Access
  * -----------------------------------
  * MCIA register is used to access the SFP+ and QSFP connector's EPROM.
@@ -8046,13 +8158,41 @@ MLXSW_ITEM32(reg, mcia, device_address, 0x04, 0, 16);
  */
 MLXSW_ITEM32(reg, mcia, size, 0x08, 0, 16);
 
-#define MLXSW_SP_REG_MCIA_EEPROM_SIZE 48
+#define MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH      256
+#define MLXSW_REG_MCIA_EEPROM_SIZE             48
+#define MLXSW_REG_MCIA_I2C_ADDR_LOW            0x50
+#define MLXSW_REG_MCIA_I2C_ADDR_HIGH           0x51
+#define MLXSW_REG_MCIA_PAGE0_LO_OFF            0xa0
+#define MLXSW_REG_MCIA_TH_ITEM_SIZE            2
+#define MLXSW_REG_MCIA_TH_PAGE_NUM             3
+#define MLXSW_REG_MCIA_PAGE0_LO                        0
+#define MLXSW_REG_MCIA_TH_PAGE_OFF             0x80
+
+enum mlxsw_reg_mcia_eeprom_module_info_rev_id {
+       MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_UNSPC  = 0x00,
+       MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8436   = 0x01,
+       MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636   = 0x03,
+};
+
+enum mlxsw_reg_mcia_eeprom_module_info_id {
+       MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP        = 0x03,
+       MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP       = 0x0C,
+       MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS  = 0x0D,
+       MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28     = 0x11,
+       MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD    = 0x18,
+};
+
+enum mlxsw_reg_mcia_eeprom_module_info {
+       MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID,
+       MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID,
+       MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE,
+};
 
 /* reg_mcia_eeprom
  * Bytes to read/write.
  * Access: RW
  */
-MLXSW_ITEM_BUF(reg, mcia, eeprom, 0x10, MLXSW_SP_REG_MCIA_EEPROM_SIZE);
+MLXSW_ITEM_BUF(reg, mcia, eeprom, 0x10, MLXSW_REG_MCIA_EEPROM_SIZE);
 
 static inline void mlxsw_reg_mcia_pack(char *payload, u8 module, u8 lock,
                                       u8 page_number, u16 device_addr,
@@ -9740,8 +9880,10 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
        MLXSW_REG(mfsc),
        MLXSW_REG(mfsm),
        MLXSW_REG(mfsl),
+       MLXSW_REG(fore),
        MLXSW_REG(mtcap),
        MLXSW_REG(mtmp),
+       MLXSW_REG(mtbr),
        MLXSW_REG(mcia),
        MLXSW_REG(mpat),
        MLXSW_REG(mpar),
index a881697..abac923 100644 (file)
@@ -1700,6 +1700,18 @@ static int mlxsw_sp_set_features(struct net_device *dev,
                                       mlxsw_sp_feature_hw_tc);
 }
 
+static int mlxsw_sp_port_get_port_parent_id(struct net_device *dev,
+                                           struct netdev_phys_item_id *ppid)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+
+       ppid->id_len = sizeof(mlxsw_sp->base_mac);
+       memcpy(&ppid->id, &mlxsw_sp->base_mac, ppid->id_len);
+
+       return 0;
+}
+
 static const struct net_device_ops mlxsw_sp_port_netdev_ops = {
        .ndo_open               = mlxsw_sp_port_open,
        .ndo_stop               = mlxsw_sp_port_stop,
@@ -1715,6 +1727,7 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = {
        .ndo_vlan_rx_kill_vid   = mlxsw_sp_port_kill_vid,
        .ndo_get_phys_port_name = mlxsw_sp_port_get_phys_port_name,
        .ndo_set_features       = mlxsw_sp_set_features,
+       .ndo_get_port_parent_id = mlxsw_sp_port_get_port_parent_id,
 };
 
 static void mlxsw_sp_port_get_drvinfo(struct net_device *dev,
@@ -2710,23 +2723,23 @@ static int mlxsw_sp_query_module_eeprom(struct mlxsw_sp_port *mlxsw_sp_port,
                                        unsigned int *p_read_size)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-       char eeprom_tmp[MLXSW_SP_REG_MCIA_EEPROM_SIZE];
+       char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
        char mcia_pl[MLXSW_REG_MCIA_LEN];
        u16 i2c_addr;
        int status;
        int err;
 
-       size = min_t(u16, size, MLXSW_SP_REG_MCIA_EEPROM_SIZE);
+       size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
 
-       if (offset < MLXSW_SP_EEPROM_PAGE_LENGTH &&
-           offset + size > MLXSW_SP_EEPROM_PAGE_LENGTH)
+       if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
+           offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
                /* Cross pages read, read until offset 256 in low page */
-               size = MLXSW_SP_EEPROM_PAGE_LENGTH - offset;
+               size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
 
-       i2c_addr = MLXSW_SP_I2C_ADDR_LOW;
-       if (offset >= MLXSW_SP_EEPROM_PAGE_LENGTH) {
-               i2c_addr = MLXSW_SP_I2C_ADDR_HIGH;
-               offset -= MLXSW_SP_EEPROM_PAGE_LENGTH;
+       i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
+       if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
+               i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
+               offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
        }
 
        mlxsw_reg_mcia_pack(mcia_pl, mlxsw_sp_port->mapping.module,
@@ -2747,55 +2760,37 @@ static int mlxsw_sp_query_module_eeprom(struct mlxsw_sp_port *mlxsw_sp_port,
        return 0;
 }
 
-enum mlxsw_sp_eeprom_module_info_rev_id {
-       MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_UNSPC      = 0x00,
-       MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_8436       = 0x01,
-       MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_8636       = 0x03,
-};
-
-enum mlxsw_sp_eeprom_module_info_id {
-       MLXSW_SP_EEPROM_MODULE_INFO_ID_SFP              = 0x03,
-       MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP             = 0x0C,
-       MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP_PLUS        = 0x0D,
-       MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP28           = 0x11,
-};
-
-enum mlxsw_sp_eeprom_module_info {
-       MLXSW_SP_EEPROM_MODULE_INFO_ID,
-       MLXSW_SP_EEPROM_MODULE_INFO_REV_ID,
-       MLXSW_SP_EEPROM_MODULE_INFO_SIZE,
-};
-
 static int mlxsw_sp_get_module_info(struct net_device *netdev,
                                    struct ethtool_modinfo *modinfo)
 {
        struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
-       u8 module_info[MLXSW_SP_EEPROM_MODULE_INFO_SIZE];
+       u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
+       u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
        u8 module_rev_id, module_id;
        unsigned int read_size;
        int err;
 
-       err = mlxsw_sp_query_module_eeprom(mlxsw_sp_port, 0,
-                                          MLXSW_SP_EEPROM_MODULE_INFO_SIZE,
+       err = mlxsw_sp_query_module_eeprom(mlxsw_sp_port, 0, offset,
                                           module_info, &read_size);
        if (err)
                return err;
 
-       if (read_size < MLXSW_SP_EEPROM_MODULE_INFO_SIZE)
+       if (read_size < offset)
                return -EIO;
 
-       module_rev_id = module_info[MLXSW_SP_EEPROM_MODULE_INFO_REV_ID];
-       module_id = module_info[MLXSW_SP_EEPROM_MODULE_INFO_ID];
+       module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
+       module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
 
        switch (module_id) {
-       case MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP:
+       case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
                modinfo->type       = ETH_MODULE_SFF_8436;
                modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
                break;
-       case MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
-       case MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP28:
-               if (module_id  == MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP28 ||
-                   module_rev_id >= MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_8636) {
+       case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
+       case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
+               if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
+                   module_rev_id >=
+                   MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
                        modinfo->type       = ETH_MODULE_SFF_8636;
                        modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
                } else {
@@ -2803,7 +2798,7 @@ static int mlxsw_sp_get_module_info(struct net_device *netdev,
                        modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
                }
                break;
-       case MLXSW_SP_EEPROM_MODULE_INFO_ID_SFP:
+       case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
                modinfo->type       = ETH_MODULE_SFF_8472;
                modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
                break;
@@ -4400,6 +4395,71 @@ static void mlxsw_sp_params_unregister(struct mlxsw_core *mlxsw_core)
                                  ARRAY_SIZE(mlxsw_sp_devlink_params));
 }
 
+static int
+mlxsw_sp_params_acl_region_rehash_intrvl_get(struct devlink *devlink, u32 id,
+                                            struct devlink_param_gset_ctx *ctx)
+{
+       struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
+
+       ctx->val.vu32 = mlxsw_sp_acl_region_rehash_intrvl_get(mlxsw_sp);
+       return 0;
+}
+
+static int
+mlxsw_sp_params_acl_region_rehash_intrvl_set(struct devlink *devlink, u32 id,
+                                            struct devlink_param_gset_ctx *ctx)
+{
+       struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
+       struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
+
+       return mlxsw_sp_acl_region_rehash_intrvl_set(mlxsw_sp, ctx->val.vu32);
+}
+
+static const struct devlink_param mlxsw_sp2_devlink_params[] = {
+       DEVLINK_PARAM_DRIVER(MLXSW_DEVLINK_PARAM_ID_ACL_REGION_REHASH_INTERVAL,
+                            "acl_region_rehash_interval",
+                            DEVLINK_PARAM_TYPE_U32,
+                            BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+                            mlxsw_sp_params_acl_region_rehash_intrvl_get,
+                            mlxsw_sp_params_acl_region_rehash_intrvl_set,
+                            NULL),
+};
+
+static int mlxsw_sp2_params_register(struct mlxsw_core *mlxsw_core)
+{
+       struct devlink *devlink = priv_to_devlink(mlxsw_core);
+       union devlink_param_value value;
+       int err;
+
+       err = mlxsw_sp_params_register(mlxsw_core);
+       if (err)
+               return err;
+
+       err = devlink_params_register(devlink, mlxsw_sp2_devlink_params,
+                                     ARRAY_SIZE(mlxsw_sp2_devlink_params));
+       if (err)
+               goto err_devlink_params_register;
+
+       value.vu32 = 0;
+       devlink_param_driverinit_value_set(devlink,
+                                          MLXSW_DEVLINK_PARAM_ID_ACL_REGION_REHASH_INTERVAL,
+                                          value);
+       return 0;
+
+err_devlink_params_register:
+       mlxsw_sp_params_unregister(mlxsw_core);
+       return err;
+}
+
+static void mlxsw_sp2_params_unregister(struct mlxsw_core *mlxsw_core)
+{
+       devlink_params_unregister(priv_to_devlink(mlxsw_core),
+                                 mlxsw_sp2_devlink_params,
+                                 ARRAY_SIZE(mlxsw_sp2_devlink_params));
+       mlxsw_sp_params_unregister(mlxsw_core);
+}
+
 static struct mlxsw_driver mlxsw_sp1_driver = {
        .kind                           = mlxsw_sp1_driver_name,
        .priv_size                      = sizeof(struct mlxsw_sp),
@@ -4448,8 +4508,8 @@ static struct mlxsw_driver mlxsw_sp2_driver = {
        .sb_occ_tc_port_bind_get        = mlxsw_sp_sb_occ_tc_port_bind_get,
        .txhdr_construct                = mlxsw_sp_txhdr_construct,
        .resources_register             = mlxsw_sp2_resources_register,
-       .params_register                = mlxsw_sp_params_register,
-       .params_unregister              = mlxsw_sp_params_unregister,
+       .params_register                = mlxsw_sp2_params_register,
+       .params_unregister              = mlxsw_sp2_params_unregister,
        .txhdr_len                      = MLXSW_TXHDR_LEN,
        .profile                        = &mlxsw_sp2_config_profile,
        .res_query_enabled              = true,
@@ -4693,9 +4753,6 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
        err = mlxsw_sp_lag_col_port_add(mlxsw_sp_port, lag_id, port_index);
        if (err)
                goto err_col_port_add;
-       err = mlxsw_sp_lag_col_port_enable(mlxsw_sp_port, lag_id);
-       if (err)
-               goto err_col_port_enable;
 
        mlxsw_core_lag_mapping_set(mlxsw_sp->core, lag_id, port_index,
                                   mlxsw_sp_port->local_port);
@@ -4709,8 +4766,6 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
 
        return 0;
 
-err_col_port_enable:
-       mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
 err_col_port_add:
        if (!lag->ref_count)
                mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
@@ -4729,7 +4784,6 @@ static void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
        lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
        WARN_ON(lag->ref_count == 0);
 
-       mlxsw_sp_lag_col_port_disable(mlxsw_sp_port, lag_id);
        mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
 
        /* Any VLANs configured on the port are no longer valid */
@@ -4774,21 +4828,56 @@ static int mlxsw_sp_lag_dist_port_remove(struct mlxsw_sp_port *mlxsw_sp_port,
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
 }
 
-static int mlxsw_sp_port_lag_tx_en_set(struct mlxsw_sp_port *mlxsw_sp_port,
-                                      bool lag_tx_enabled)
+static int
+mlxsw_sp_port_lag_col_dist_enable(struct mlxsw_sp_port *mlxsw_sp_port)
 {
-       if (lag_tx_enabled)
-               return mlxsw_sp_lag_dist_port_add(mlxsw_sp_port,
-                                                 mlxsw_sp_port->lag_id);
-       else
-               return mlxsw_sp_lag_dist_port_remove(mlxsw_sp_port,
-                                                    mlxsw_sp_port->lag_id);
+       int err;
+
+       err = mlxsw_sp_lag_col_port_enable(mlxsw_sp_port,
+                                          mlxsw_sp_port->lag_id);
+       if (err)
+               return err;
+
+       err = mlxsw_sp_lag_dist_port_add(mlxsw_sp_port, mlxsw_sp_port->lag_id);
+       if (err)
+               goto err_dist_port_add;
+
+       return 0;
+
+err_dist_port_add:
+       mlxsw_sp_lag_col_port_disable(mlxsw_sp_port, mlxsw_sp_port->lag_id);
+       return err;
+}
+
+static int
+mlxsw_sp_port_lag_col_dist_disable(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       int err;
+
+       err = mlxsw_sp_lag_dist_port_remove(mlxsw_sp_port,
+                                           mlxsw_sp_port->lag_id);
+       if (err)
+               return err;
+
+       err = mlxsw_sp_lag_col_port_disable(mlxsw_sp_port,
+                                           mlxsw_sp_port->lag_id);
+       if (err)
+               goto err_col_port_disable;
+
+       return 0;
+
+err_col_port_disable:
+       mlxsw_sp_lag_dist_port_add(mlxsw_sp_port, mlxsw_sp_port->lag_id);
+       return err;
 }
 
 static int mlxsw_sp_port_lag_changed(struct mlxsw_sp_port *mlxsw_sp_port,
                                     struct netdev_lag_lower_state_info *info)
 {
-       return mlxsw_sp_port_lag_tx_en_set(mlxsw_sp_port, info->tx_enabled);
+       if (info->tx_enabled)
+               return mlxsw_sp_port_lag_col_dist_enable(mlxsw_sp_port);
+       else
+               return mlxsw_sp_port_lag_col_dist_disable(mlxsw_sp_port);
 }
 
 static int mlxsw_sp_port_stp_set(struct mlxsw_sp_port *mlxsw_sp_port,
@@ -5011,8 +5100,7 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
                                err = mlxsw_sp_port_lag_join(mlxsw_sp_port,
                                                             upper_dev);
                        } else {
-                               mlxsw_sp_port_lag_tx_en_set(mlxsw_sp_port,
-                                                           false);
+                               mlxsw_sp_port_lag_col_dist_disable(mlxsw_sp_port);
                                mlxsw_sp_port_lag_leave(mlxsw_sp_port,
                                                        upper_dev);
                        }
index 4fe0996..ceebc91 100644 (file)
@@ -690,6 +690,8 @@ struct mlxsw_sp_fid *mlxsw_sp_acl_dummy_fid(struct mlxsw_sp *mlxsw_sp);
 
 int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp);
 void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp);
+u32 mlxsw_sp_acl_region_rehash_intrvl_get(struct mlxsw_sp *mlxsw_sp);
+int mlxsw_sp_acl_region_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp, u32 val);
 
 /* spectrum_acl_tcam.c */
 struct mlxsw_sp_acl_tcam;
@@ -704,10 +706,13 @@ struct mlxsw_sp_acl_tcam_ops {
        size_t region_priv_size;
        int (*region_init)(struct mlxsw_sp *mlxsw_sp, void *region_priv,
                           void *tcam_priv,
-                          struct mlxsw_sp_acl_tcam_region *region);
+                          struct mlxsw_sp_acl_tcam_region *region,
+                          void *hints_priv);
        void (*region_fini)(struct mlxsw_sp *mlxsw_sp, void *region_priv);
        int (*region_associate)(struct mlxsw_sp *mlxsw_sp,
                                struct mlxsw_sp_acl_tcam_region *region);
+       void * (*region_rehash_hints_get)(void *region_priv);
+       void (*region_rehash_hints_put)(void *hints_priv);
        size_t chunk_priv_size;
        void (*chunk_init)(void *region_priv, void *chunk_priv,
                           unsigned int priority);
index 6e44452..3a636f7 100644 (file)
@@ -112,7 +112,8 @@ mlxsw_sp1_acl_ctcam_region_catchall_del(struct mlxsw_sp *mlxsw_sp,
 static int
 mlxsw_sp1_acl_tcam_region_init(struct mlxsw_sp *mlxsw_sp, void *region_priv,
                               void *tcam_priv,
-                              struct mlxsw_sp_acl_tcam_region *_region)
+                              struct mlxsw_sp_acl_tcam_region *_region,
+                              void *hints_priv)
 {
        struct mlxsw_sp1_acl_tcam_region *region = region_priv;
        int err;
index d380b34..6c66a0f 100644 (file)
@@ -139,7 +139,8 @@ static void mlxsw_sp2_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp, void *priv)
 static int
 mlxsw_sp2_acl_tcam_region_init(struct mlxsw_sp *mlxsw_sp, void *region_priv,
                               void *tcam_priv,
-                              struct mlxsw_sp_acl_tcam_region *_region)
+                              struct mlxsw_sp_acl_tcam_region *_region,
+                              void *hints_priv)
 {
        struct mlxsw_sp2_acl_tcam_region *region = region_priv;
        struct mlxsw_sp2_acl_tcam *tcam = tcam_priv;
@@ -147,7 +148,8 @@ mlxsw_sp2_acl_tcam_region_init(struct mlxsw_sp *mlxsw_sp, void *region_priv,
        region->region = _region;
 
        return mlxsw_sp_acl_atcam_region_init(mlxsw_sp, &tcam->atcam,
-                                             &region->aregion, _region,
+                                             &region->aregion,
+                                             _region, hints_priv,
                                              &mlxsw_sp2_acl_ctcam_region_ops);
 }
 
@@ -166,6 +168,18 @@ mlxsw_sp2_acl_tcam_region_associate(struct mlxsw_sp *mlxsw_sp,
        return mlxsw_sp_acl_atcam_region_associate(mlxsw_sp, region->id);
 }
 
+static void *mlxsw_sp2_acl_tcam_region_rehash_hints_get(void *region_priv)
+{
+       struct mlxsw_sp2_acl_tcam_region *region = region_priv;
+
+       return mlxsw_sp_acl_atcam_rehash_hints_get(&region->aregion);
+}
+
+static void mlxsw_sp2_acl_tcam_region_rehash_hints_put(void *hints_priv)
+{
+       mlxsw_sp_acl_atcam_rehash_hints_put(hints_priv);
+}
+
 static void mlxsw_sp2_acl_tcam_chunk_init(void *region_priv, void *chunk_priv,
                                          unsigned int priority)
 {
@@ -243,6 +257,8 @@ const struct mlxsw_sp_acl_tcam_ops mlxsw_sp2_acl_tcam_ops = {
        .region_init            = mlxsw_sp2_acl_tcam_region_init,
        .region_fini            = mlxsw_sp2_acl_tcam_region_fini,
        .region_associate       = mlxsw_sp2_acl_tcam_region_associate,
+       .region_rehash_hints_get = mlxsw_sp2_acl_tcam_region_rehash_hints_get,
+       .region_rehash_hints_put = mlxsw_sp2_acl_tcam_region_rehash_hints_put,
        .chunk_priv_size        = sizeof(struct mlxsw_sp2_acl_tcam_chunk),
        .chunk_init             = mlxsw_sp2_acl_tcam_chunk_init,
        .chunk_fini             = mlxsw_sp2_acl_tcam_chunk_fini,
index a69e346..a146a44 100644 (file)
@@ -588,7 +588,7 @@ int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp,
 {
        u8 ethertype;
 
-       if (action == TCA_VLAN_ACT_MODIFY) {
+       if (action == FLOW_ACTION_VLAN_MANGLE) {
                switch (proto) {
                case ETH_P_8021Q:
                        ethertype = 0;
@@ -640,7 +640,7 @@ mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
        int err;
 
        mlxsw_sp_acl_ruleset_ref_inc(ruleset);
-       rule = kzalloc(sizeof(*rule) + ops->rule_priv_size(mlxsw_sp),
+       rule = kzalloc(sizeof(*rule) + ops->rule_priv_size,
                       GFP_KERNEL);
        if (!rule) {
                err = -ENOMEM;
@@ -912,3 +912,19 @@ void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp)
        mlxsw_afk_destroy(acl->afk);
        kfree(acl);
 }
+
+u32 mlxsw_sp_acl_region_rehash_intrvl_get(struct mlxsw_sp *mlxsw_sp)
+{
+       struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
+
+       return mlxsw_sp_acl_tcam_vregion_rehash_intrvl_get(mlxsw_sp,
+                                                          &acl->tcam);
+}
+
+int mlxsw_sp_acl_region_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp, u32 val)
+{
+       struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
+
+       return mlxsw_sp_acl_tcam_vregion_rehash_intrvl_set(mlxsw_sp,
+                                                          &acl->tcam, val);
+}
index 40dc76a..ded4cf6 100644 (file)
@@ -7,6 +7,8 @@
 #include <linux/gfp.h>
 #include <linux/refcount.h>
 #include <linux/rhashtable.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/mlxsw.h>
 
 #include "reg.h"
 #include "core.h"
@@ -316,6 +318,7 @@ mlxsw_sp_acl_atcam_region_init(struct mlxsw_sp *mlxsw_sp,
                               struct mlxsw_sp_acl_atcam *atcam,
                               struct mlxsw_sp_acl_atcam_region *aregion,
                               struct mlxsw_sp_acl_tcam_region *region,
+                              void *hints_priv,
                               const struct mlxsw_sp_acl_ctcam_region_ops *ops)
 {
        int err;
@@ -332,7 +335,7 @@ mlxsw_sp_acl_atcam_region_init(struct mlxsw_sp *mlxsw_sp,
        err = aregion->ops->init(aregion);
        if (err)
                goto err_ops_init;
-       err = mlxsw_sp_acl_erp_region_init(aregion);
+       err = mlxsw_sp_acl_erp_region_init(aregion, hints_priv);
        if (err)
                goto err_erp_region_init;
        err = mlxsw_sp_acl_ctcam_region_init(mlxsw_sp, &aregion->cregion,
@@ -390,8 +393,7 @@ mlxsw_sp_acl_atcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
        if (err)
                return err;
 
-       lkey_id = aregion->ops->lkey_id_get(aregion, aentry->ht_key.enc_key,
-                                           erp_id);
+       lkey_id = aregion->ops->lkey_id_get(aregion, aentry->enc_key, erp_id);
        if (IS_ERR(lkey_id))
                return PTR_ERR(lkey_id);
        aentry->lkey_id = lkey_id;
@@ -399,7 +401,7 @@ mlxsw_sp_acl_atcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
        kvdl_index = mlxsw_afa_block_first_kvdl_index(rulei->act_block);
        mlxsw_reg_ptce3_pack(ptce3_pl, true, MLXSW_REG_PTCE3_OP_WRITE_WRITE,
                             priority, region->tcam_region_info,
-                            aentry->ht_key.enc_key, erp_id,
+                            aentry->enc_key, erp_id,
                             aentry->delta_info.start,
                             aentry->delta_info.mask,
                             aentry->delta_info.value,
@@ -424,12 +426,11 @@ mlxsw_sp_acl_atcam_region_entry_remove(struct mlxsw_sp *mlxsw_sp,
        struct mlxsw_sp_acl_atcam_lkey_id *lkey_id = aentry->lkey_id;
        struct mlxsw_sp_acl_tcam_region *region = aregion->region;
        u8 erp_id = mlxsw_sp_acl_erp_mask_erp_id(aentry->erp_mask);
-       char *enc_key = aentry->ht_key.enc_key;
        char ptce3_pl[MLXSW_REG_PTCE3_LEN];
 
        mlxsw_reg_ptce3_pack(ptce3_pl, false, MLXSW_REG_PTCE3_OP_WRITE_WRITE, 0,
                             region->tcam_region_info,
-                            enc_key, erp_id,
+                            aentry->enc_key, erp_id,
                             aentry->delta_info.start,
                             aentry->delta_info.mask,
                             aentry->delta_info.value,
@@ -458,7 +459,7 @@ mlxsw_sp_acl_atcam_region_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
        kvdl_index = mlxsw_afa_block_first_kvdl_index(rulei->act_block);
        mlxsw_reg_ptce3_pack(ptce3_pl, true, MLXSW_REG_PTCE3_OP_WRITE_UPDATE,
                             priority, region->tcam_region_info,
-                            aentry->ht_key.enc_key, erp_id,
+                            aentry->enc_key, erp_id,
                             aentry->delta_info.start,
                             aentry->delta_info.mask,
                             aentry->delta_info.value,
@@ -481,15 +482,15 @@ __mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
        int err;
 
        mlxsw_afk_encode(afk, region->key_info, &rulei->values,
-                        aentry->full_enc_key, mask);
+                        aentry->ht_key.full_enc_key, mask);
 
        erp_mask = mlxsw_sp_acl_erp_mask_get(aregion, mask, false);
        if (IS_ERR(erp_mask))
                return PTR_ERR(erp_mask);
        aentry->erp_mask = erp_mask;
        aentry->ht_key.erp_id = mlxsw_sp_acl_erp_mask_erp_id(erp_mask);
-       memcpy(aentry->ht_key.enc_key, aentry->full_enc_key,
-              sizeof(aentry->ht_key.enc_key));
+       memcpy(aentry->enc_key, aentry->ht_key.full_enc_key,
+              sizeof(aentry->enc_key));
 
        /* Compute all needed delta information and clear the delta bits
         * from the encrypted key.
@@ -498,8 +499,9 @@ __mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
        aentry->delta_info.start = mlxsw_sp_acl_erp_delta_start(delta);
        aentry->delta_info.mask = mlxsw_sp_acl_erp_delta_mask(delta);
        aentry->delta_info.value =
-               mlxsw_sp_acl_erp_delta_value(delta, aentry->full_enc_key);
-       mlxsw_sp_acl_erp_delta_clear(delta, aentry->ht_key.enc_key);
+               mlxsw_sp_acl_erp_delta_value(delta,
+                                            aentry->ht_key.full_enc_key);
+       mlxsw_sp_acl_erp_delta_clear(delta, aentry->enc_key);
 
        /* Add rule to the list of A-TCAM rules, assuming this
         * rule is intended to A-TCAM. In case this rule does
@@ -579,6 +581,7 @@ int mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
        /* It is possible we failed to add the rule to the A-TCAM due to
         * exceeded number of masks. Try to spill into C-TCAM.
         */
+       trace_mlxsw_sp_acl_atcam_entry_add_ctcam_spill(mlxsw_sp, aregion);
        err = mlxsw_sp_acl_ctcam_entry_add(mlxsw_sp, &aregion->cregion,
                                           &achunk->cchunk, &aentry->centry,
                                           rulei, true);
@@ -632,3 +635,14 @@ void mlxsw_sp_acl_atcam_fini(struct mlxsw_sp *mlxsw_sp,
 {
        mlxsw_sp_acl_erps_fini(mlxsw_sp, atcam);
 }
+
+void *
+mlxsw_sp_acl_atcam_rehash_hints_get(struct mlxsw_sp_acl_atcam_region *aregion)
+{
+       return mlxsw_sp_acl_erp_rehash_hints_get(aregion);
+}
+
+void mlxsw_sp_acl_atcam_rehash_hints_put(void *hints_priv)
+{
+       mlxsw_sp_acl_erp_rehash_hints_put(hints_priv);
+}
index f5c381d..9545b57 100644 (file)
@@ -133,7 +133,7 @@ mlxsw_sp_acl_bf_key_encode(struct mlxsw_sp_acl_atcam_region *aregion,
                memcpy(chunk + MLXSW_BLOOM_CHUNK_PAD_BYTES, &erp_region_id,
                       sizeof(erp_region_id));
                memcpy(chunk + MLXSW_BLOOM_CHUNK_KEY_OFFSET,
-                      &aentry->ht_key.enc_key[chunk_key_offsets[chunk_index]],
+                      &aentry->enc_key[chunk_key_offsets[chunk_index]],
                       MLXSW_BLOOM_CHUNK_KEY_BYTES);
                chunk += MLXSW_BLOOM_KEY_CHUNK_BYTES;
        }
index 2941967..e935c36 100644 (file)
@@ -1200,6 +1200,32 @@ mlxsw_sp_acl_erp_delta_fill(const struct mlxsw_sp_acl_erp_key *parent_key,
        return 0;
 }
 
+static bool mlxsw_sp_acl_erp_delta_check(void *priv, const void *parent_obj,
+                                        const void *obj)
+{
+       const struct mlxsw_sp_acl_erp_key *parent_key = parent_obj;
+       const struct mlxsw_sp_acl_erp_key *key = obj;
+       u16 delta_start;
+       u8 delta_mask;
+       int err;
+
+       err = mlxsw_sp_acl_erp_delta_fill(parent_key, key,
+                                         &delta_start, &delta_mask);
+       return err ? false : true;
+}
+
+static int mlxsw_sp_acl_erp_hints_obj_cmp(const void *obj1, const void *obj2)
+{
+       const struct mlxsw_sp_acl_erp_key *key1 = obj1;
+       const struct mlxsw_sp_acl_erp_key *key2 = obj2;
+
+       /* For hints purposes, two objects are considered equal
+        * in case the masks are the same. Does not matter what
+        * the "ctcam" value is.
+        */
+       return memcmp(key1->mask, key2->mask, sizeof(key1->mask));
+}
+
 static void *mlxsw_sp_acl_erp_delta_create(void *priv, void *parent_obj,
                                           void *obj)
 {
@@ -1254,12 +1280,17 @@ static void mlxsw_sp_acl_erp_delta_destroy(void *priv, void *delta_priv)
        kfree(delta);
 }
 
-static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj)
+static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj,
+                                         unsigned int root_id)
 {
        struct mlxsw_sp_acl_atcam_region *aregion = priv;
        struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
        struct mlxsw_sp_acl_erp_key *key = obj;
 
+       if (!key->ctcam &&
+           root_id != OBJAGG_OBJ_ROOT_ID_INVALID &&
+           root_id >= MLXSW_SP_ACL_ERP_MAX_PER_REGION)
+               return ERR_PTR(-ENOBUFS);
        return erp_table->ops->erp_create(erp_table, key);
 }
 
@@ -1273,6 +1304,8 @@ static void mlxsw_sp_acl_erp_root_destroy(void *priv, void *root_priv)
 
 static const struct objagg_ops mlxsw_sp_acl_erp_objagg_ops = {
        .obj_size = sizeof(struct mlxsw_sp_acl_erp_key),
+       .delta_check = mlxsw_sp_acl_erp_delta_check,
+       .hints_obj_cmp = mlxsw_sp_acl_erp_hints_obj_cmp,
        .delta_create = mlxsw_sp_acl_erp_delta_create,
        .delta_destroy = mlxsw_sp_acl_erp_delta_destroy,
        .root_create = mlxsw_sp_acl_erp_root_create,
@@ -1280,7 +1313,8 @@ static const struct objagg_ops mlxsw_sp_acl_erp_objagg_ops = {
 };
 
 static struct mlxsw_sp_acl_erp_table *
-mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
+mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion,
+                             struct objagg_hints *hints)
 {
        struct mlxsw_sp_acl_erp_table *erp_table;
        int err;
@@ -1290,7 +1324,7 @@ mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
                return ERR_PTR(-ENOMEM);
 
        erp_table->objagg = objagg_create(&mlxsw_sp_acl_erp_objagg_ops,
-                                         aregion);
+                                         hints, aregion);
        if (IS_ERR(erp_table->objagg)) {
                err = PTR_ERR(erp_table->objagg);
                goto err_objagg_create;
@@ -1337,12 +1371,88 @@ mlxsw_sp_acl_erp_region_param_init(struct mlxsw_sp_acl_atcam_region *aregion)
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
 }
 
-int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion)
+static int
+mlxsw_sp_acl_erp_hints_check(struct mlxsw_sp *mlxsw_sp,
+                            struct mlxsw_sp_acl_atcam_region *aregion,
+                            struct objagg_hints *hints, bool *p_rehash_needed)
+{
+       struct objagg *objagg = aregion->erp_table->objagg;
+       const struct objagg_stats *ostats;
+       const struct objagg_stats *hstats;
+       int err;
+
+       *p_rehash_needed = false;
+
+       ostats = objagg_stats_get(objagg);
+       if (IS_ERR(ostats)) {
+               dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get ERP stats\n");
+               return PTR_ERR(ostats);
+       }
+
+       hstats = objagg_hints_stats_get(hints);
+       if (IS_ERR(hstats)) {
+               dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get ERP hints stats\n");
+               err = PTR_ERR(hstats);
+               goto err_hints_stats_get;
+       }
+
+       /* Very basic criterion for now. */
+       if (hstats->root_count < ostats->root_count)
+               *p_rehash_needed = true;
+
+       err = 0;
+
+       objagg_stats_put(hstats);
+err_hints_stats_get:
+       objagg_stats_put(ostats);
+       return err;
+}
+
+void *
+mlxsw_sp_acl_erp_rehash_hints_get(struct mlxsw_sp_acl_atcam_region *aregion)
+{
+       struct mlxsw_sp *mlxsw_sp = aregion->region->mlxsw_sp;
+       struct objagg_hints *hints;
+       bool rehash_needed;
+       int err;
+
+       hints = objagg_hints_get(aregion->erp_table->objagg,
+                                OBJAGG_OPT_ALGO_SIMPLE_GREEDY);
+       if (IS_ERR(hints)) {
+               dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to create ERP hints\n");
+               return ERR_CAST(hints);
+       }
+       err = mlxsw_sp_acl_erp_hints_check(mlxsw_sp, aregion, hints,
+                                          &rehash_needed);
+       if (err)
+               goto errout;
+
+       if (!rehash_needed) {
+               err = -EAGAIN;
+               goto errout;
+       }
+       return hints;
+
+errout:
+       objagg_hints_put(hints);
+       return ERR_PTR(err);
+}
+
+void mlxsw_sp_acl_erp_rehash_hints_put(void *hints_priv)
+{
+       struct objagg_hints *hints = hints_priv;
+
+       objagg_hints_put(hints);
+}
+
+int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion,
+                                void *hints_priv)
 {
        struct mlxsw_sp_acl_erp_table *erp_table;
+       struct objagg_hints *hints = hints_priv;
        int err;
 
-       erp_table = mlxsw_sp_acl_erp_table_create(aregion);
+       erp_table = mlxsw_sp_acl_erp_table_create(aregion, hints);
        if (IS_ERR(erp_table))
                return PTR_ERR(erp_table);
        aregion->erp_table = erp_table;
index 11456e1..7e225a8 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/list.h>
 #include <linux/rhashtable.h>
 #include <linux/netdevice.h>
+#include <trace/events/mlxsw.h>
 
 #include "reg.h"
 #include "core.h"
@@ -23,6 +24,9 @@ size_t mlxsw_sp_acl_tcam_priv_size(struct mlxsw_sp *mlxsw_sp)
        return ops->priv_size;
 }
 
+#define MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_DFLT 5000 /* ms */
+#define MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_MIN 3000 /* ms */
+
 int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp,
                           struct mlxsw_sp_acl_tcam *tcam)
 {
@@ -33,6 +37,10 @@ int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp,
        size_t alloc_size;
        int err;
 
+       tcam->vregion_rehash_intrvl =
+                       MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_DFLT;
+       INIT_LIST_HEAD(&tcam->vregion_list);
+
        max_tcam_regions = MLXSW_CORE_RES_GET(mlxsw_sp->core,
                                              ACL_MAX_TCAM_REGIONS);
        max_regions = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_REGIONS);
@@ -153,9 +161,9 @@ struct mlxsw_sp_acl_tcam_pattern {
 struct mlxsw_sp_acl_tcam_group {
        struct mlxsw_sp_acl_tcam *tcam;
        u16 id;
-       struct list_head region_list;
+       struct list_head vregion_list;
        unsigned int region_count;
-       struct rhashtable chunk_ht;
+       struct rhashtable vchunk_ht;
        struct mlxsw_sp_acl_tcam_group_ops *ops;
        const struct mlxsw_sp_acl_tcam_pattern *patterns;
        unsigned int patterns_count;
@@ -163,40 +171,77 @@ struct mlxsw_sp_acl_tcam_group {
        struct mlxsw_afk_element_usage tmplt_elusage;
 };
 
-struct mlxsw_sp_acl_tcam_chunk {
-       struct list_head list; /* Member of a TCAM region */
-       struct rhash_head ht_node; /* Member of a chunk HT */
-       unsigned int priority; /* Priority within the region and group */
+struct mlxsw_sp_acl_tcam_vregion {
+       struct mlxsw_sp_acl_tcam_region *region;
+       struct mlxsw_sp_acl_tcam_region *region2; /* Used during migration */
+       struct list_head list; /* Member of a TCAM group */
+       struct list_head tlist; /* Member of a TCAM */
+       struct list_head vchunk_list; /* List of vchunks under this vregion */
        struct mlxsw_sp_acl_tcam_group *group;
+       struct mlxsw_afk_key_info *key_info;
+       struct mlxsw_sp_acl_tcam *tcam;
+       struct delayed_work rehash_dw;
+       struct mlxsw_sp *mlxsw_sp;
+       bool failed_rollback; /* Indicates failed rollback during migration */
+};
+
+struct mlxsw_sp_acl_tcam_vchunk;
+
+struct mlxsw_sp_acl_tcam_chunk {
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk;
        struct mlxsw_sp_acl_tcam_region *region;
-       unsigned int ref_count;
        unsigned long priv[0];
        /* priv has to be always the last item */
 };
 
+struct mlxsw_sp_acl_tcam_vchunk {
+       struct mlxsw_sp_acl_tcam_chunk *chunk;
+       struct mlxsw_sp_acl_tcam_chunk *chunk2; /* Used during migration */
+       struct list_head list; /* Member of a TCAM vregion */
+       struct rhash_head ht_node; /* Member of a chunk HT */
+       struct list_head ventry_list;
+       unsigned int priority; /* Priority within the vregion and group */
+       struct mlxsw_sp_acl_tcam_group *group;
+       struct mlxsw_sp_acl_tcam_vregion *vregion;
+       unsigned int ref_count;
+};
+
 struct mlxsw_sp_acl_tcam_entry {
+       struct mlxsw_sp_acl_tcam_ventry *ventry;
        struct mlxsw_sp_acl_tcam_chunk *chunk;
        unsigned long priv[0];
        /* priv has to be always the last item */
 };
 
-static const struct rhashtable_params mlxsw_sp_acl_tcam_chunk_ht_params = {
+struct mlxsw_sp_acl_tcam_ventry {
+       struct mlxsw_sp_acl_tcam_entry *entry;
+       struct list_head list; /* Member of a TCAM vchunk */
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk;
+       struct mlxsw_sp_acl_rule_info *rulei;
+};
+
+static const struct rhashtable_params mlxsw_sp_acl_tcam_vchunk_ht_params = {
        .key_len = sizeof(unsigned int),
-       .key_offset = offsetof(struct mlxsw_sp_acl_tcam_chunk, priority),
-       .head_offset = offsetof(struct mlxsw_sp_acl_tcam_chunk, ht_node),
+       .key_offset = offsetof(struct mlxsw_sp_acl_tcam_vchunk, priority),
+       .head_offset = offsetof(struct mlxsw_sp_acl_tcam_vchunk, ht_node),
        .automatic_shrinking = true,
 };
 
 static int mlxsw_sp_acl_tcam_group_update(struct mlxsw_sp *mlxsw_sp,
                                          struct mlxsw_sp_acl_tcam_group *group)
 {
-       struct mlxsw_sp_acl_tcam_region *region;
+       struct mlxsw_sp_acl_tcam_vregion *vregion;
        char pagt_pl[MLXSW_REG_PAGT_LEN];
        int acl_index = 0;
 
        mlxsw_reg_pagt_pack(pagt_pl, group->id);
-       list_for_each_entry(region, &group->region_list, list)
-               mlxsw_reg_pagt_acl_id_pack(pagt_pl, acl_index++, region->id);
+       list_for_each_entry(vregion, &group->vregion_list, list) {
+               if (vregion->region2)
+                       mlxsw_reg_pagt_acl_id_pack(pagt_pl, acl_index++,
+                                                  vregion->region2->id, true);
+               mlxsw_reg_pagt_acl_id_pack(pagt_pl, acl_index++,
+                                          vregion->region->id, false);
+       }
        mlxsw_reg_pagt_size_set(pagt_pl, acl_index);
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pagt), pagt_pl);
 }
@@ -219,13 +264,13 @@ mlxsw_sp_acl_tcam_group_add(struct mlxsw_sp *mlxsw_sp,
                memcpy(&group->tmplt_elusage, tmplt_elusage,
                       sizeof(group->tmplt_elusage));
        }
-       INIT_LIST_HEAD(&group->region_list);
+       INIT_LIST_HEAD(&group->vregion_list);
        err = mlxsw_sp_acl_tcam_group_id_get(tcam, &group->id);
        if (err)
                return err;
 
-       err = rhashtable_init(&group->chunk_ht,
-                             &mlxsw_sp_acl_tcam_chunk_ht_params);
+       err = rhashtable_init(&group->vchunk_ht,
+                             &mlxsw_sp_acl_tcam_vchunk_ht_params);
        if (err)
                goto err_rhashtable_init;
 
@@ -241,9 +286,9 @@ static void mlxsw_sp_acl_tcam_group_del(struct mlxsw_sp *mlxsw_sp,
 {
        struct mlxsw_sp_acl_tcam *tcam = group->tcam;
 
-       rhashtable_destroy(&group->chunk_ht);
+       rhashtable_destroy(&group->vchunk_ht);
        mlxsw_sp_acl_tcam_group_id_put(tcam, group->id);
-       WARN_ON(!list_empty(&group->region_list));
+       WARN_ON(!list_empty(&group->vregion_list));
 }
 
 static int
@@ -283,140 +328,153 @@ mlxsw_sp_acl_tcam_group_id(struct mlxsw_sp_acl_tcam_group *group)
 }
 
 static unsigned int
-mlxsw_sp_acl_tcam_region_prio(struct mlxsw_sp_acl_tcam_region *region)
+mlxsw_sp_acl_tcam_vregion_prio(struct mlxsw_sp_acl_tcam_vregion *vregion)
 {
-       struct mlxsw_sp_acl_tcam_chunk *chunk;
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk;
 
-       if (list_empty(&region->chunk_list))
+       if (list_empty(&vregion->vchunk_list))
                return 0;
-       /* As a priority of a region, return priority of the first chunk */
-       chunk = list_first_entry(&region->chunk_list, typeof(*chunk), list);
-       return chunk->priority;
+       /* As a priority of a vregion, return priority of the first vchunk */
+       vchunk = list_first_entry(&vregion->vchunk_list,
+                                 typeof(*vchunk), list);
+       return vchunk->priority;
 }
 
 static unsigned int
-mlxsw_sp_acl_tcam_region_max_prio(struct mlxsw_sp_acl_tcam_region *region)
+mlxsw_sp_acl_tcam_vregion_max_prio(struct mlxsw_sp_acl_tcam_vregion *vregion)
 {
-       struct mlxsw_sp_acl_tcam_chunk *chunk;
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk;
 
-       if (list_empty(&region->chunk_list))
+       if (list_empty(&vregion->vchunk_list))
                return 0;
-       chunk = list_last_entry(&region->chunk_list, typeof(*chunk), list);
-       return chunk->priority;
+       vchunk = list_last_entry(&vregion->vchunk_list,
+                                typeof(*vchunk), list);
+       return vchunk->priority;
 }
 
-static void
-mlxsw_sp_acl_tcam_group_list_add(struct mlxsw_sp_acl_tcam_group *group,
-                                struct mlxsw_sp_acl_tcam_region *region)
+static int
+mlxsw_sp_acl_tcam_group_region_attach(struct mlxsw_sp *mlxsw_sp,
+                                     struct mlxsw_sp_acl_tcam_region *region)
 {
-       struct mlxsw_sp_acl_tcam_region *region2;
-       struct list_head *pos;
+       struct mlxsw_sp_acl_tcam_group *group = region->vregion->group;
+       int err;
+
+       if (group->region_count == group->tcam->max_group_size)
+               return -ENOBUFS;
+
+       err = mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);
+       if (err)
+               return err;
 
-       /* Position the region inside the list according to priority */
-       list_for_each(pos, &group->region_list) {
-               region2 = list_entry(pos, typeof(*region2), list);
-               if (mlxsw_sp_acl_tcam_region_prio(region2) >
-                   mlxsw_sp_acl_tcam_region_prio(region))
-                       break;
-       }
-       list_add_tail(&region->list, pos);
        group->region_count++;
+       return 0;
 }
 
 static void
-mlxsw_sp_acl_tcam_group_list_del(struct mlxsw_sp_acl_tcam_group *group,
-                                struct mlxsw_sp_acl_tcam_region *region)
+mlxsw_sp_acl_tcam_group_region_detach(struct mlxsw_sp *mlxsw_sp,
+                                     struct mlxsw_sp_acl_tcam_region *region)
 {
+       struct mlxsw_sp_acl_tcam_group *group = region->vregion->group;
+
        group->region_count--;
-       list_del(&region->list);
+       mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);
 }
 
 static int
-mlxsw_sp_acl_tcam_group_region_attach(struct mlxsw_sp *mlxsw_sp,
-                                     struct mlxsw_sp_acl_tcam_group *group,
-                                     struct mlxsw_sp_acl_tcam_region *region)
+mlxsw_sp_acl_tcam_group_vregion_attach(struct mlxsw_sp *mlxsw_sp,
+                                      struct mlxsw_sp_acl_tcam_group *group,
+                                      struct mlxsw_sp_acl_tcam_vregion *vregion)
 {
+       struct mlxsw_sp_acl_tcam_vregion *vregion2;
+       struct list_head *pos;
        int err;
 
-       if (group->region_count == group->tcam->max_group_size)
-               return -ENOBUFS;
-
-       mlxsw_sp_acl_tcam_group_list_add(group, region);
+       /* Position the vregion inside the list according to priority */
+       list_for_each(pos, &group->vregion_list) {
+               vregion2 = list_entry(pos, typeof(*vregion2), list);
+               if (mlxsw_sp_acl_tcam_vregion_prio(vregion2) >
+                   mlxsw_sp_acl_tcam_vregion_prio(vregion))
+                       break;
+       }
+       list_add_tail(&vregion->list, pos);
+       vregion->group = group;
 
-       err = mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);
+       err = mlxsw_sp_acl_tcam_group_region_attach(mlxsw_sp, vregion->region);
        if (err)
-               goto err_group_update;
-       region->group = group;
+               goto err_region_attach;
 
        return 0;
 
-err_group_update:
-       mlxsw_sp_acl_tcam_group_list_del(group, region);
-       mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);
+err_region_attach:
+       list_del(&vregion->list);
        return err;
 }
 
 static void
-mlxsw_sp_acl_tcam_group_region_detach(struct mlxsw_sp *mlxsw_sp,
-                                     struct mlxsw_sp_acl_tcam_region *region)
+mlxsw_sp_acl_tcam_group_vregion_detach(struct mlxsw_sp *mlxsw_sp,
+                                      struct mlxsw_sp_acl_tcam_vregion *vregion)
 {
-       struct mlxsw_sp_acl_tcam_group *group = region->group;
-
-       mlxsw_sp_acl_tcam_group_list_del(group, region);
-       mlxsw_sp_acl_tcam_group_update(mlxsw_sp, group);
+       list_del(&vregion->list);
+       if (vregion->region2)
+               mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp,
+                                                     vregion->region2);
+       mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp, vregion->region);
 }
 
-static struct mlxsw_sp_acl_tcam_region *
-mlxsw_sp_acl_tcam_group_region_find(struct mlxsw_sp_acl_tcam_group *group,
-                                   unsigned int priority,
-                                   struct mlxsw_afk_element_usage *elusage,
-                                   bool *p_need_split)
+static struct mlxsw_sp_acl_tcam_vregion *
+mlxsw_sp_acl_tcam_group_vregion_find(struct mlxsw_sp_acl_tcam_group *group,
+                                    unsigned int priority,
+                                    struct mlxsw_afk_element_usage *elusage,
+                                    bool *p_need_split)
 {
-       struct mlxsw_sp_acl_tcam_region *region, *region2;
+       struct mlxsw_sp_acl_tcam_vregion *vregion, *vregion2;
        struct list_head *pos;
        bool issubset;
 
-       list_for_each(pos, &group->region_list) {
-               region = list_entry(pos, typeof(*region), list);
+       list_for_each(pos, &group->vregion_list) {
+               vregion = list_entry(pos, typeof(*vregion), list);
 
                /* First, check if the requested priority does not rather belong
-                * under some of the next regions.
+                * under some of the next vregions.
                 */
-               if (pos->next != &group->region_list) { /* not last */
-                       region2 = list_entry(pos->next, typeof(*region2), list);
-                       if (priority >= mlxsw_sp_acl_tcam_region_prio(region2))
+               if (pos->next != &group->vregion_list) { /* not last */
+                       vregion2 = list_entry(pos->next, typeof(*vregion2),
+                                             list);
+                       if (priority >=
+                           mlxsw_sp_acl_tcam_vregion_prio(vregion2))
                                continue;
                }
 
-               issubset = mlxsw_afk_key_info_subset(region->key_info, elusage);
+               issubset = mlxsw_afk_key_info_subset(vregion->key_info,
+                                                    elusage);
 
                /* If requested element usage would not fit and the priority
-                * is lower than the currently inspected region we cannot
-                * use this region, so return NULL to indicate new region has
+                * is lower than the currently inspected vregion we cannot
+                * use this region, so return NULL to indicate new vregion has
                 * to be created.
                 */
                if (!issubset &&
-                   priority < mlxsw_sp_acl_tcam_region_prio(region))
+                   priority < mlxsw_sp_acl_tcam_vregion_prio(vregion))
                        return NULL;
 
                /* If requested element usage would not fit and the priority
-                * is higher than the currently inspected region we cannot
-                * use this region. There is still some hope that the next
-                * region would be the fit. So let it be processed and
+                * is higher than the currently inspected vregion we cannot
+                * use this vregion. There is still some hope that the next
+                * vregion would be the fit. So let it be processed and
                 * eventually break at the check right above this.
                 */
                if (!issubset &&
-                   priority > mlxsw_sp_acl_tcam_region_max_prio(region))
+                   priority > mlxsw_sp_acl_tcam_vregion_max_prio(vregion))
                        continue;
 
-               /* Indicate if the region needs to be split in order to add
+               /* Indicate if the vregion needs to be split in order to add
                 * the requested priority. Split is needed when requested
-                * element usage won't fit into the found region.
+                * element usage won't fit into the found vregion.
                 */
                *p_need_split = !issubset;
-               return region;
+               return vregion;
        }
-       return NULL; /* New region has to be created. */
+       return NULL; /* New vregion has to be created. */
 }
 
 static void
@@ -511,24 +569,19 @@ mlxsw_sp_acl_tcam_region_disable(struct mlxsw_sp *mlxsw_sp,
 static struct mlxsw_sp_acl_tcam_region *
 mlxsw_sp_acl_tcam_region_create(struct mlxsw_sp *mlxsw_sp,
                                struct mlxsw_sp_acl_tcam *tcam,
-                               struct mlxsw_afk_element_usage *elusage)
+                               struct mlxsw_sp_acl_tcam_vregion *vregion,
+                               void *hints_priv)
 {
        const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
-       struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);
        struct mlxsw_sp_acl_tcam_region *region;
        int err;
 
        region = kzalloc(sizeof(*region) + ops->region_priv_size, GFP_KERNEL);
        if (!region)
                return ERR_PTR(-ENOMEM);
-       INIT_LIST_HEAD(&region->chunk_list);
        region->mlxsw_sp = mlxsw_sp;
-
-       region->key_info = mlxsw_afk_key_info_get(afk, elusage);
-       if (IS_ERR(region->key_info)) {
-               err = PTR_ERR(region->key_info);
-               goto err_key_info_get;
-       }
+       region->vregion = vregion;
+       region->key_info = vregion->key_info;
 
        err = mlxsw_sp_acl_tcam_region_id_get(tcam, &region->id);
        if (err)
@@ -547,7 +600,8 @@ mlxsw_sp_acl_tcam_region_create(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_tcam_region_enable;
 
-       err = ops->region_init(mlxsw_sp, region->priv, tcam->priv, region);
+       err = ops->region_init(mlxsw_sp, region->priv, tcam->priv,
+                              region, hints_priv);
        if (err)
                goto err_tcam_region_init;
 
@@ -561,8 +615,6 @@ err_tcam_region_alloc:
 err_tcam_region_associate:
        mlxsw_sp_acl_tcam_region_id_put(tcam, region->id);
 err_region_id_get:
-       mlxsw_afk_key_info_put(region->key_info);
-err_key_info_get:
        kfree(region);
        return ERR_PTR(err);
 }
@@ -576,217 +628,372 @@ mlxsw_sp_acl_tcam_region_destroy(struct mlxsw_sp *mlxsw_sp,
        ops->region_fini(mlxsw_sp, region->priv);
        mlxsw_sp_acl_tcam_region_disable(mlxsw_sp, region);
        mlxsw_sp_acl_tcam_region_free(mlxsw_sp, region);
-       mlxsw_sp_acl_tcam_region_id_put(region->group->tcam, region->id);
-       mlxsw_afk_key_info_put(region->key_info);
+       mlxsw_sp_acl_tcam_region_id_put(region->vregion->group->tcam,
+                                       region->id);
        kfree(region);
 }
 
+static void
+mlxsw_sp_acl_tcam_vregion_rehash_work_schedule(struct mlxsw_sp_acl_tcam_vregion *vregion)
+{
+       unsigned long interval = vregion->tcam->vregion_rehash_intrvl;
+
+       if (!interval)
+               return;
+       mlxsw_core_schedule_dw(&vregion->rehash_dw,
+                              msecs_to_jiffies(interval));
+}
+
 static int
-mlxsw_sp_acl_tcam_chunk_assoc(struct mlxsw_sp *mlxsw_sp,
-                             struct mlxsw_sp_acl_tcam_group *group,
-                             unsigned int priority,
-                             struct mlxsw_afk_element_usage *elusage,
-                             struct mlxsw_sp_acl_tcam_chunk *chunk)
+mlxsw_sp_acl_tcam_vregion_rehash(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_acl_tcam_vregion *vregion);
+
+static void mlxsw_sp_acl_tcam_vregion_rehash_work(struct work_struct *work)
 {
-       struct mlxsw_sp_acl_tcam_region *region;
-       bool region_created = false;
+       struct mlxsw_sp_acl_tcam_vregion *vregion =
+               container_of(work, struct mlxsw_sp_acl_tcam_vregion,
+                            rehash_dw.work);
+
+       /* TODO: Take rtnl lock here as the rest of the code counts on it
+        * now. Later, this should be replaced by per-vregion lock.
+        */
+       rtnl_lock();
+       mlxsw_sp_acl_tcam_vregion_rehash(vregion->mlxsw_sp, vregion);
+       rtnl_unlock();
+       mlxsw_sp_acl_tcam_vregion_rehash_work_schedule(vregion);
+}
+
+static struct mlxsw_sp_acl_tcam_vregion *
+mlxsw_sp_acl_tcam_vregion_create(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_acl_tcam *tcam,
+                                struct mlxsw_afk_element_usage *elusage)
+{
+       const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
+       struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);
+       struct mlxsw_sp_acl_tcam_vregion *vregion;
+       int err;
+
+       vregion = kzalloc(sizeof(*vregion), GFP_KERNEL);
+       if (!vregion)
+               return ERR_PTR(-ENOMEM);
+       INIT_LIST_HEAD(&vregion->vchunk_list);
+       vregion->tcam = tcam;
+       vregion->mlxsw_sp = mlxsw_sp;
+
+       vregion->key_info = mlxsw_afk_key_info_get(afk, elusage);
+       if (IS_ERR(vregion->key_info)) {
+               err = PTR_ERR(vregion->key_info);
+               goto err_key_info_get;
+       }
+
+       vregion->region = mlxsw_sp_acl_tcam_region_create(mlxsw_sp, tcam,
+                                                         vregion, NULL);
+       if (IS_ERR(vregion->region)) {
+               err = PTR_ERR(vregion->region);
+               goto err_region_create;
+       }
+
+       list_add_tail(&vregion->tlist, &tcam->vregion_list);
+
+       if (ops->region_rehash_hints_get) {
+               /* Create the delayed work for vregion periodic rehash */
+               INIT_DELAYED_WORK(&vregion->rehash_dw,
+                                 mlxsw_sp_acl_tcam_vregion_rehash_work);
+               mlxsw_sp_acl_tcam_vregion_rehash_work_schedule(vregion);
+       }
+
+       return vregion;
+
+err_region_create:
+       mlxsw_afk_key_info_put(vregion->key_info);
+err_key_info_get:
+       kfree(vregion);
+       return ERR_PTR(err);
+}
+
+static void
+mlxsw_sp_acl_tcam_vregion_destroy(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_acl_tcam_vregion *vregion)
+{
+       const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
+
+       if (ops->region_rehash_hints_get)
+               cancel_delayed_work_sync(&vregion->rehash_dw);
+       list_del(&vregion->tlist);
+       if (vregion->region2)
+               mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, vregion->region2);
+       mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, vregion->region);
+       mlxsw_afk_key_info_put(vregion->key_info);
+       kfree(vregion);
+}
+
+u32 mlxsw_sp_acl_tcam_vregion_rehash_intrvl_get(struct mlxsw_sp *mlxsw_sp,
+                                               struct mlxsw_sp_acl_tcam *tcam)
+{
+       const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
+       u32 vregion_rehash_intrvl;
+
+       if (WARN_ON(!ops->region_rehash_hints_get))
+               return 0;
+       vregion_rehash_intrvl = tcam->vregion_rehash_intrvl;
+       return vregion_rehash_intrvl;
+}
+
+int mlxsw_sp_acl_tcam_vregion_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp,
+                                               struct mlxsw_sp_acl_tcam *tcam,
+                                               u32 val)
+{
+       const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
+       struct mlxsw_sp_acl_tcam_vregion *vregion;
+
+       if (val < MLXSW_SP_ACL_TCAM_VREGION_REHASH_INTRVL_MIN && val)
+               return -EINVAL;
+       if (WARN_ON(!ops->region_rehash_hints_get))
+               return -EOPNOTSUPP;
+       tcam->vregion_rehash_intrvl = val;
+       rtnl_lock();
+       list_for_each_entry(vregion, &tcam->vregion_list, tlist) {
+               if (val)
+                       mlxsw_core_schedule_dw(&vregion->rehash_dw, 0);
+               else
+                       cancel_delayed_work_sync(&vregion->rehash_dw);
+       }
+       rtnl_unlock();
+       return 0;
+}
+
+static int
+mlxsw_sp_acl_tcam_vchunk_assoc(struct mlxsw_sp *mlxsw_sp,
+                              struct mlxsw_sp_acl_tcam_group *group,
+                              unsigned int priority,
+                              struct mlxsw_afk_element_usage *elusage,
+                              struct mlxsw_sp_acl_tcam_vchunk *vchunk)
+{
+       struct mlxsw_sp_acl_tcam_vregion *vregion;
+       bool vregion_created = false;
        bool need_split;
        int err;
 
-       region = mlxsw_sp_acl_tcam_group_region_find(group, priority, elusage,
-                                                    &need_split);
-       if (region && need_split) {
-               /* According to priority, the chunk should belong to an
-                * existing region. However, this chunk needs elements
-                * that region does not contain. We need to split the existing
-                * region into two and create a new region for this chunk
+       vregion = mlxsw_sp_acl_tcam_group_vregion_find(group, priority, elusage,
+                                                      &need_split);
+       if (vregion && need_split) {
+               /* According to priority, the vchunk should belong to an
+                * existing vregion. However, this vchunk needs elements
+                * that vregion does not contain. We need to split the existing
+                * vregion into two and create a new vregion for this vchunk
                 * in between. This is not supported now.
                 */
                return -EOPNOTSUPP;
        }
-       if (!region) {
-               struct mlxsw_afk_element_usage region_elusage;
+       if (!vregion) {
+               struct mlxsw_afk_element_usage vregion_elusage;
 
                mlxsw_sp_acl_tcam_group_use_patterns(group, elusage,
-                                                    &region_elusage);
-               region = mlxsw_sp_acl_tcam_region_create(mlxsw_sp, group->tcam,
-                                                        &region_elusage);
-               if (IS_ERR(region))
-                       return PTR_ERR(region);
-               region_created = true;
+                                                    &vregion_elusage);
+               vregion = mlxsw_sp_acl_tcam_vregion_create(mlxsw_sp,
+                                                          group->tcam,
+                                                          &vregion_elusage);
+               if (IS_ERR(vregion))
+                       return PTR_ERR(vregion);
+               vregion_created = true;
        }
 
-       chunk->region = region;
-       list_add_tail(&chunk->list, &region->chunk_list);
+       vchunk->vregion = vregion;
+       list_add_tail(&vchunk->list, &vregion->vchunk_list);
 
-       if (!region_created)
+       if (!vregion_created)
                return 0;
 
-       err = mlxsw_sp_acl_tcam_group_region_attach(mlxsw_sp, group, region);
+       err = mlxsw_sp_acl_tcam_group_vregion_attach(mlxsw_sp, group, vregion);
        if (err)
-               goto err_group_region_attach;
+               goto err_group_vregion_attach;
 
        return 0;
 
-err_group_region_attach:
-       mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, region);
+err_group_vregion_attach:
+       mlxsw_sp_acl_tcam_vregion_destroy(mlxsw_sp, vregion);
        return err;
 }
 
 static void
-mlxsw_sp_acl_tcam_chunk_deassoc(struct mlxsw_sp *mlxsw_sp,
-                               struct mlxsw_sp_acl_tcam_chunk *chunk)
+mlxsw_sp_acl_tcam_vchunk_deassoc(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_acl_tcam_vchunk *vchunk)
 {
-       struct mlxsw_sp_acl_tcam_region *region = chunk->region;
+       struct mlxsw_sp_acl_tcam_vregion *vregion = vchunk->vregion;
 
-       list_del(&chunk->list);
-       if (list_empty(&region->chunk_list)) {
-               mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp, region);
-               mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, region);
+       list_del(&vchunk->list);
+       if (list_empty(&vregion->vchunk_list)) {
+               mlxsw_sp_acl_tcam_group_vregion_detach(mlxsw_sp, vregion);
+               mlxsw_sp_acl_tcam_vregion_destroy(mlxsw_sp, vregion);
        }
 }
 
 static struct mlxsw_sp_acl_tcam_chunk *
 mlxsw_sp_acl_tcam_chunk_create(struct mlxsw_sp *mlxsw_sp,
-                              struct mlxsw_sp_acl_tcam_group *group,
-                              unsigned int priority,
-                              struct mlxsw_afk_element_usage *elusage)
+                              struct mlxsw_sp_acl_tcam_vchunk *vchunk,
+                              struct mlxsw_sp_acl_tcam_region *region)
 {
        const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
        struct mlxsw_sp_acl_tcam_chunk *chunk;
+
+       chunk = kzalloc(sizeof(*chunk) + ops->chunk_priv_size, GFP_KERNEL);
+       if (!chunk)
+               return ERR_PTR(-ENOMEM);
+       chunk->vchunk = vchunk;
+       chunk->region = region;
+
+       ops->chunk_init(region->priv, chunk->priv, vchunk->priority);
+       return chunk;
+}
+
+static void
+mlxsw_sp_acl_tcam_chunk_destroy(struct mlxsw_sp *mlxsw_sp,
+                               struct mlxsw_sp_acl_tcam_chunk *chunk)
+{
+       const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
+
+       ops->chunk_fini(chunk->priv);
+       kfree(chunk);
+}
+
+static struct mlxsw_sp_acl_tcam_vchunk *
+mlxsw_sp_acl_tcam_vchunk_create(struct mlxsw_sp *mlxsw_sp,
+                               struct mlxsw_sp_acl_tcam_group *group,
+                               unsigned int priority,
+                               struct mlxsw_afk_element_usage *elusage)
+{
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk;
        int err;
 
        if (priority == MLXSW_SP_ACL_TCAM_CATCHALL_PRIO)
                return ERR_PTR(-EINVAL);
 
-       chunk = kzalloc(sizeof(*chunk) + ops->chunk_priv_size, GFP_KERNEL);
-       if (!chunk)
+       vchunk = kzalloc(sizeof(*vchunk), GFP_KERNEL);
+       if (!vchunk)
                return ERR_PTR(-ENOMEM);
-       chunk->priority = priority;
-       chunk->group = group;
-       chunk->ref_count = 1;
+       INIT_LIST_HEAD(&vchunk->ventry_list);
+       vchunk->priority = priority;
+       vchunk->group = group;
+       vchunk->ref_count = 1;
 
-       err = mlxsw_sp_acl_tcam_chunk_assoc(mlxsw_sp, group, priority,
-                                           elusage, chunk);
+       err = mlxsw_sp_acl_tcam_vchunk_assoc(mlxsw_sp, group, priority,
+                                            elusage, vchunk);
        if (err)
-               goto err_chunk_assoc;
+               goto err_vchunk_assoc;
 
-       ops->chunk_init(chunk->region->priv, chunk->priv, priority);
-
-       err = rhashtable_insert_fast(&group->chunk_ht, &chunk->ht_node,
-                                    mlxsw_sp_acl_tcam_chunk_ht_params);
+       err = rhashtable_insert_fast(&group->vchunk_ht, &vchunk->ht_node,
+                                    mlxsw_sp_acl_tcam_vchunk_ht_params);
        if (err)
                goto err_rhashtable_insert;
 
-       return chunk;
+       vchunk->chunk = mlxsw_sp_acl_tcam_chunk_create(mlxsw_sp, vchunk,
+                                                      vchunk->vregion->region);
+       if (IS_ERR(vchunk->chunk)) {
+               err = PTR_ERR(vchunk->chunk);
+               goto err_chunk_create;
+       }
+
+       return vchunk;
 
+err_chunk_create:
+       rhashtable_remove_fast(&group->vchunk_ht, &vchunk->ht_node,
+                              mlxsw_sp_acl_tcam_vchunk_ht_params);
 err_rhashtable_insert:
-       ops->chunk_fini(chunk->priv);
-       mlxsw_sp_acl_tcam_chunk_deassoc(mlxsw_sp, chunk);
-err_chunk_assoc:
-       kfree(chunk);
+       mlxsw_sp_acl_tcam_vchunk_deassoc(mlxsw_sp, vchunk);
+err_vchunk_assoc:
+       kfree(vchunk);
        return ERR_PTR(err);
 }
 
 static void
-mlxsw_sp_acl_tcam_chunk_destroy(struct mlxsw_sp *mlxsw_sp,
-                               struct mlxsw_sp_acl_tcam_chunk *chunk)
+mlxsw_sp_acl_tcam_vchunk_destroy(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_acl_tcam_vchunk *vchunk)
 {
-       const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
-       struct mlxsw_sp_acl_tcam_group *group = chunk->group;
-
-       rhashtable_remove_fast(&group->chunk_ht, &chunk->ht_node,
-                              mlxsw_sp_acl_tcam_chunk_ht_params);
-       ops->chunk_fini(chunk->priv);
-       mlxsw_sp_acl_tcam_chunk_deassoc(mlxsw_sp, chunk);
-       kfree(chunk);
+       struct mlxsw_sp_acl_tcam_group *group = vchunk->group;
+
+       if (vchunk->chunk2)
+               mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, vchunk->chunk2);
+       mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, vchunk->chunk);
+       rhashtable_remove_fast(&group->vchunk_ht, &vchunk->ht_node,
+                              mlxsw_sp_acl_tcam_vchunk_ht_params);
+       mlxsw_sp_acl_tcam_vchunk_deassoc(mlxsw_sp, vchunk);
+       kfree(vchunk);
 }
 
-static struct mlxsw_sp_acl_tcam_chunk *
-mlxsw_sp_acl_tcam_chunk_get(struct mlxsw_sp *mlxsw_sp,
-                           struct mlxsw_sp_acl_tcam_group *group,
-                           unsigned int priority,
-                           struct mlxsw_afk_element_usage *elusage)
+static struct mlxsw_sp_acl_tcam_vchunk *
+mlxsw_sp_acl_tcam_vchunk_get(struct mlxsw_sp *mlxsw_sp,
+                            struct mlxsw_sp_acl_tcam_group *group,
+                            unsigned int priority,
+                            struct mlxsw_afk_element_usage *elusage)
 {
-       struct mlxsw_sp_acl_tcam_chunk *chunk;
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk;
 
-       chunk = rhashtable_lookup_fast(&group->chunk_ht, &priority,
-                                      mlxsw_sp_acl_tcam_chunk_ht_params);
-       if (chunk) {
-               if (WARN_ON(!mlxsw_afk_key_info_subset(chunk->region->key_info,
+       vchunk = rhashtable_lookup_fast(&group->vchunk_ht, &priority,
+                                       mlxsw_sp_acl_tcam_vchunk_ht_params);
+       if (vchunk) {
+               if (WARN_ON(!mlxsw_afk_key_info_subset(vchunk->vregion->key_info,
                                                       elusage)))
                        return ERR_PTR(-EINVAL);
-               chunk->ref_count++;
-               return chunk;
+               vchunk->ref_count++;
+               return vchunk;
        }
-       return mlxsw_sp_acl_tcam_chunk_create(mlxsw_sp, group,
-                                             priority, elusage);
+       return mlxsw_sp_acl_tcam_vchunk_create(mlxsw_sp, group,
+                                              priority, elusage);
 }
 
-static void mlxsw_sp_acl_tcam_chunk_put(struct mlxsw_sp *mlxsw_sp,
-                                       struct mlxsw_sp_acl_tcam_chunk *chunk)
+static void
+mlxsw_sp_acl_tcam_vchunk_put(struct mlxsw_sp *mlxsw_sp,
+                            struct mlxsw_sp_acl_tcam_vchunk *vchunk)
 {
-       if (--chunk->ref_count)
+       if (--vchunk->ref_count)
                return;
-       mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, chunk);
+       mlxsw_sp_acl_tcam_vchunk_destroy(mlxsw_sp, vchunk);
 }
 
-static size_t mlxsw_sp_acl_tcam_entry_priv_size(struct mlxsw_sp *mlxsw_sp)
-{
-       const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
-
-       return ops->entry_priv_size;
-}
-
-static int mlxsw_sp_acl_tcam_entry_add(struct mlxsw_sp *mlxsw_sp,
-                                      struct mlxsw_sp_acl_tcam_group *group,
-                                      struct mlxsw_sp_acl_tcam_entry *entry,
-                                      struct mlxsw_sp_acl_rule_info *rulei)
+static struct mlxsw_sp_acl_tcam_entry *
+mlxsw_sp_acl_tcam_entry_create(struct mlxsw_sp *mlxsw_sp,
+                              struct mlxsw_sp_acl_tcam_ventry *ventry,
+                              struct mlxsw_sp_acl_tcam_chunk *chunk)
 {
        const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
-       struct mlxsw_sp_acl_tcam_chunk *chunk;
-       struct mlxsw_sp_acl_tcam_region *region;
+       struct mlxsw_sp_acl_tcam_entry *entry;
        int err;
 
-       chunk = mlxsw_sp_acl_tcam_chunk_get(mlxsw_sp, group, rulei->priority,
-                                           &rulei->values.elusage);
-       if (IS_ERR(chunk))
-               return PTR_ERR(chunk);
-
-       region = chunk->region;
+       entry = kzalloc(sizeof(*entry) + ops->entry_priv_size, GFP_KERNEL);
+       if (!entry)
+               return ERR_PTR(-ENOMEM);
+       entry->ventry = ventry;
+       entry->chunk = chunk;
 
-       err = ops->entry_add(mlxsw_sp, region->priv, chunk->priv,
-                            entry->priv, rulei);
+       err = ops->entry_add(mlxsw_sp, chunk->region->priv, chunk->priv,
+                            entry->priv, ventry->rulei);
        if (err)
                goto err_entry_add;
-       entry->chunk = chunk;
 
-       return 0;
+       return entry;
 
 err_entry_add:
-       mlxsw_sp_acl_tcam_chunk_put(mlxsw_sp, chunk);
-       return err;
+       kfree(entry);
+       return ERR_PTR(err);
 }
 
-static void mlxsw_sp_acl_tcam_entry_del(struct mlxsw_sp *mlxsw_sp,
-                                       struct mlxsw_sp_acl_tcam_entry *entry)
+static void mlxsw_sp_acl_tcam_entry_destroy(struct mlxsw_sp *mlxsw_sp,
+                                           struct mlxsw_sp_acl_tcam_entry *entry)
 {
        const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
-       struct mlxsw_sp_acl_tcam_chunk *chunk = entry->chunk;
-       struct mlxsw_sp_acl_tcam_region *region = chunk->region;
 
-       ops->entry_del(mlxsw_sp, region->priv, chunk->priv, entry->priv);
-       mlxsw_sp_acl_tcam_chunk_put(mlxsw_sp, chunk);
+       ops->entry_del(mlxsw_sp, entry->chunk->region->priv,
+                      entry->chunk->priv, entry->priv);
+       kfree(entry);
 }
 
 static int
 mlxsw_sp_acl_tcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                      struct mlxsw_sp_acl_tcam_region *region,
                                       struct mlxsw_sp_acl_tcam_entry *entry,
                                       struct mlxsw_sp_acl_rule_info *rulei)
 {
        const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
-       struct mlxsw_sp_acl_tcam_chunk *chunk = entry->chunk;
-       struct mlxsw_sp_acl_tcam_region *region = chunk->region;
 
        return ops->entry_action_replace(mlxsw_sp, region->priv,
                                         entry->priv, rulei);
@@ -798,13 +1005,249 @@ mlxsw_sp_acl_tcam_entry_activity_get(struct mlxsw_sp *mlxsw_sp,
                                     bool *activity)
 {
        const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
-       struct mlxsw_sp_acl_tcam_chunk *chunk = entry->chunk;
-       struct mlxsw_sp_acl_tcam_region *region = chunk->region;
 
-       return ops->entry_activity_get(mlxsw_sp, region->priv,
+       return ops->entry_activity_get(mlxsw_sp, entry->chunk->region->priv,
                                       entry->priv, activity);
 }
 
+static int mlxsw_sp_acl_tcam_ventry_add(struct mlxsw_sp *mlxsw_sp,
+                                       struct mlxsw_sp_acl_tcam_group *group,
+                                       struct mlxsw_sp_acl_tcam_ventry *ventry,
+                                       struct mlxsw_sp_acl_rule_info *rulei)
+{
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk;
+       int err;
+
+       vchunk = mlxsw_sp_acl_tcam_vchunk_get(mlxsw_sp, group, rulei->priority,
+                                             &rulei->values.elusage);
+       if (IS_ERR(vchunk))
+               return PTR_ERR(vchunk);
+
+       ventry->vchunk = vchunk;
+       ventry->rulei = rulei;
+       ventry->entry = mlxsw_sp_acl_tcam_entry_create(mlxsw_sp, ventry,
+                                                      vchunk->chunk);
+       if (IS_ERR(ventry->entry)) {
+               err = PTR_ERR(ventry->entry);
+               goto err_entry_create;
+       }
+
+       list_add_tail(&ventry->list, &vchunk->ventry_list);
+
+       return 0;
+
+err_entry_create:
+       mlxsw_sp_acl_tcam_vchunk_put(mlxsw_sp, vchunk);
+       return err;
+}
+
+static void mlxsw_sp_acl_tcam_ventry_del(struct mlxsw_sp *mlxsw_sp,
+                                        struct mlxsw_sp_acl_tcam_ventry *ventry)
+{
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk = ventry->vchunk;
+
+       list_del(&ventry->list);
+       mlxsw_sp_acl_tcam_entry_destroy(mlxsw_sp, ventry->entry);
+       mlxsw_sp_acl_tcam_vchunk_put(mlxsw_sp, vchunk);
+}
+
+static int
+mlxsw_sp_acl_tcam_ventry_action_replace(struct mlxsw_sp *mlxsw_sp,
+                                       struct mlxsw_sp_acl_tcam_ventry *ventry,
+                                       struct mlxsw_sp_acl_rule_info *rulei)
+{
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk = ventry->vchunk;
+
+       return mlxsw_sp_acl_tcam_entry_action_replace(mlxsw_sp,
+                                                     vchunk->vregion->region,
+                                                     ventry->entry, rulei);
+}
+
+static int
+mlxsw_sp_acl_tcam_ventry_activity_get(struct mlxsw_sp *mlxsw_sp,
+                                     struct mlxsw_sp_acl_tcam_ventry *ventry,
+                                     bool *activity)
+{
+       return mlxsw_sp_acl_tcam_entry_activity_get(mlxsw_sp,
+                                                   ventry->entry, activity);
+}
+
+static int
+mlxsw_sp_acl_tcam_ventry_migrate(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_acl_tcam_ventry *ventry,
+                                struct mlxsw_sp_acl_tcam_chunk *chunk2)
+{
+       struct mlxsw_sp_acl_tcam_entry *entry2;
+
+       entry2 = mlxsw_sp_acl_tcam_entry_create(mlxsw_sp, ventry, chunk2);
+       if (IS_ERR(entry2))
+               return PTR_ERR(entry2);
+       mlxsw_sp_acl_tcam_entry_destroy(mlxsw_sp, ventry->entry);
+       ventry->entry = entry2;
+       return 0;
+}
+
+static int
+mlxsw_sp_acl_tcam_vchunk_migrate_one(struct mlxsw_sp *mlxsw_sp,
+                                    struct mlxsw_sp_acl_tcam_vchunk *vchunk,
+                                    struct mlxsw_sp_acl_tcam_region *region,
+                                    bool this_is_rollback)
+{
+       struct mlxsw_sp_acl_tcam_ventry *ventry;
+       struct mlxsw_sp_acl_tcam_chunk *chunk2;
+       int err;
+       int err2;
+
+       chunk2 = mlxsw_sp_acl_tcam_chunk_create(mlxsw_sp, vchunk, region);
+       if (IS_ERR(chunk2)) {
+               if (this_is_rollback)
+                       vchunk->vregion->failed_rollback = true;
+               return PTR_ERR(chunk2);
+       }
+       vchunk->chunk2 = chunk2;
+       list_for_each_entry(ventry, &vchunk->ventry_list, list) {
+               err = mlxsw_sp_acl_tcam_ventry_migrate(mlxsw_sp, ventry,
+                                                      vchunk->chunk2);
+               if (err) {
+                       if (this_is_rollback) {
+                               vchunk->vregion->failed_rollback = true;
+                               return err;
+                       }
+                       goto rollback;
+               }
+       }
+       mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, vchunk->chunk);
+       vchunk->chunk = chunk2;
+       vchunk->chunk2 = NULL;
+       return 0;
+
+rollback:
+       /* Migrate the entries back to the original chunk. If some entry
+        * migration fails, there's no good way how to proceed. Set the
+        * vregion with "failed_rollback" flag.
+        */
+       list_for_each_entry_continue_reverse(ventry, &vchunk->ventry_list,
+                                            list) {
+               err2 = mlxsw_sp_acl_tcam_ventry_migrate(mlxsw_sp, ventry,
+                                                       vchunk->chunk);
+               if (err2) {
+                       vchunk->vregion->failed_rollback = true;
+                       goto err_rollback;
+               }
+       }
+
+       mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, vchunk->chunk2);
+       vchunk->chunk2 = NULL;
+
+err_rollback:
+       return err;
+}
+
+static int
+mlxsw_sp_acl_tcam_vchunk_migrate_all(struct mlxsw_sp *mlxsw_sp,
+                                    struct mlxsw_sp_acl_tcam_vregion *vregion)
+{
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk;
+       int err;
+
+       list_for_each_entry(vchunk, &vregion->vchunk_list, list) {
+               err = mlxsw_sp_acl_tcam_vchunk_migrate_one(mlxsw_sp, vchunk,
+                                                          vregion->region2,
+                                                          false);
+               if (err)
+                       goto rollback;
+       }
+       return 0;
+
+rollback:
+       list_for_each_entry_continue_reverse(vchunk, &vregion->vchunk_list,
+                                            list) {
+               mlxsw_sp_acl_tcam_vchunk_migrate_one(mlxsw_sp, vchunk,
+                                                    vregion->region, true);
+       }
+       return err;
+}
+
+static int
+mlxsw_sp_acl_tcam_vregion_migrate(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_acl_tcam_vregion *vregion,
+                                 void *hints_priv)
+{
+       struct mlxsw_sp_acl_tcam_region *region2, *unused_region;
+       int err;
+
+       trace_mlxsw_sp_acl_tcam_vregion_migrate(mlxsw_sp, vregion);
+
+       region2 = mlxsw_sp_acl_tcam_region_create(mlxsw_sp, vregion->tcam,
+                                                 vregion, hints_priv);
+       if (IS_ERR(region2))
+               return PTR_ERR(region2);
+
+       vregion->region2 = region2;
+       err = mlxsw_sp_acl_tcam_group_region_attach(mlxsw_sp, region2);
+       if (err)
+               goto err_group_region_attach;
+
+       err = mlxsw_sp_acl_tcam_vchunk_migrate_all(mlxsw_sp, vregion);
+       if (!vregion->failed_rollback) {
+               if (!err) {
+                       /* In case of successful migration, region2 is used and
+                        * the original is unused.
+                        */
+                       unused_region = vregion->region;
+                       vregion->region = vregion->region2;
+               } else {
+                       /* In case of failure during migration, the original
+                        * region is still used.
+                        */
+                       unused_region = vregion->region2;
+               }
+               vregion->region2 = NULL;
+               mlxsw_sp_acl_tcam_group_region_detach(mlxsw_sp, unused_region);
+               mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, unused_region);
+       }
+       return err;
+
+err_group_region_attach:
+       vregion->region2 = NULL;
+       mlxsw_sp_acl_tcam_region_destroy(mlxsw_sp, region2);
+       return err;
+}
+
+static int
+mlxsw_sp_acl_tcam_vregion_rehash(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_acl_tcam_vregion *vregion)
+{
+       const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;
+       void *hints_priv;
+       int err;
+
+       trace_mlxsw_sp_acl_tcam_vregion_rehash(mlxsw_sp, vregion);
+       if (vregion->failed_rollback)
+               return -EBUSY;
+
+       hints_priv = ops->region_rehash_hints_get(vregion->region->priv);
+       if (IS_ERR(hints_priv)) {
+               err = PTR_ERR(hints_priv);
+               if (err != -EAGAIN)
+                       dev_err(mlxsw_sp->bus_info->dev, "Failed get rehash hints\n");
+               return err;
+       }
+
+       err = mlxsw_sp_acl_tcam_vregion_migrate(mlxsw_sp, vregion, hints_priv);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to migrate vregion\n");
+               if (vregion->failed_rollback) {
+                       trace_mlxsw_sp_acl_tcam_vregion_rehash_dis(mlxsw_sp,
+                                                                  vregion);
+                       dev_err(mlxsw_sp->bus_info->dev, "Failed to rollback during vregion migration fail\n");
+               }
+       }
+
+       ops->region_rehash_hints_put(hints_priv);
+       return err;
+}
+
 static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv4[] = {
        MLXSW_AFK_ELEMENT_SRC_SYS_PORT,
        MLXSW_AFK_ELEMENT_DMAC_32_47,
@@ -859,7 +1302,7 @@ struct mlxsw_sp_acl_tcam_flower_ruleset {
 };
 
 struct mlxsw_sp_acl_tcam_flower_rule {
-       struct mlxsw_sp_acl_tcam_entry entry;
+       struct mlxsw_sp_acl_tcam_ventry ventry;
 };
 
 static int
@@ -917,12 +1360,6 @@ mlxsw_sp_acl_tcam_flower_ruleset_group_id(void *ruleset_priv)
        return mlxsw_sp_acl_tcam_group_id(&ruleset->group);
 }
 
-static size_t mlxsw_sp_acl_tcam_flower_rule_priv_size(struct mlxsw_sp *mlxsw_sp)
-{
-       return sizeof(struct mlxsw_sp_acl_tcam_flower_rule) +
-              mlxsw_sp_acl_tcam_entry_priv_size(mlxsw_sp);
-}
-
 static int
 mlxsw_sp_acl_tcam_flower_rule_add(struct mlxsw_sp *mlxsw_sp,
                                  void *ruleset_priv, void *rule_priv,
@@ -931,8 +1368,8 @@ mlxsw_sp_acl_tcam_flower_rule_add(struct mlxsw_sp *mlxsw_sp,
        struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv;
        struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;
 
-       return mlxsw_sp_acl_tcam_entry_add(mlxsw_sp, &ruleset->group,
-                                          &rule->entry, rulei);
+       return mlxsw_sp_acl_tcam_ventry_add(mlxsw_sp, &ruleset->group,
+                                           &rule->ventry, rulei);
 }
 
 static void
@@ -940,7 +1377,7 @@ mlxsw_sp_acl_tcam_flower_rule_del(struct mlxsw_sp *mlxsw_sp, void *rule_priv)
 {
        struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;
 
-       mlxsw_sp_acl_tcam_entry_del(mlxsw_sp, &rule->entry);
+       mlxsw_sp_acl_tcam_ventry_del(mlxsw_sp, &rule->ventry);
 }
 
 static int
@@ -957,8 +1394,8 @@ mlxsw_sp_acl_tcam_flower_rule_activity_get(struct mlxsw_sp *mlxsw_sp,
 {
        struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv;
 
-       return mlxsw_sp_acl_tcam_entry_activity_get(mlxsw_sp, &rule->entry,
-                                                   activity);
+       return mlxsw_sp_acl_tcam_ventry_activity_get(mlxsw_sp, &rule->ventry,
+                                                    activity);
 }
 
 static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = {
@@ -968,7 +1405,7 @@ static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = {
        .ruleset_bind           = mlxsw_sp_acl_tcam_flower_ruleset_bind,
        .ruleset_unbind         = mlxsw_sp_acl_tcam_flower_ruleset_unbind,
        .ruleset_group_id       = mlxsw_sp_acl_tcam_flower_ruleset_group_id,
-       .rule_priv_size         = mlxsw_sp_acl_tcam_flower_rule_priv_size,
+       .rule_priv_size         = sizeof(struct mlxsw_sp_acl_tcam_flower_rule),
        .rule_add               = mlxsw_sp_acl_tcam_flower_rule_add,
        .rule_del               = mlxsw_sp_acl_tcam_flower_rule_del,
        .rule_action_replace    = mlxsw_sp_acl_tcam_flower_rule_action_replace,
@@ -976,12 +1413,12 @@ static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = {
 };
 
 struct mlxsw_sp_acl_tcam_mr_ruleset {
-       struct mlxsw_sp_acl_tcam_chunk *chunk;
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk;
        struct mlxsw_sp_acl_tcam_group group;
 };
 
 struct mlxsw_sp_acl_tcam_mr_rule {
-       struct mlxsw_sp_acl_tcam_entry entry;
+       struct mlxsw_sp_acl_tcam_ventry ventry;
 };
 
 static int
@@ -1006,10 +1443,11 @@ mlxsw_sp_acl_tcam_mr_ruleset_add(struct mlxsw_sp *mlxsw_sp,
         * specific ACL Group ID which must exist in HW before multicast router
         * is initialized.
         */
-       ruleset->chunk = mlxsw_sp_acl_tcam_chunk_get(mlxsw_sp, &ruleset->group,
-                                                    1, tmplt_elusage);
-       if (IS_ERR(ruleset->chunk)) {
-               err = PTR_ERR(ruleset->chunk);
+       ruleset->vchunk = mlxsw_sp_acl_tcam_vchunk_get(mlxsw_sp,
+                                                      &ruleset->group, 1,
+                                                      tmplt_elusage);
+       if (IS_ERR(ruleset->vchunk)) {
+               err = PTR_ERR(ruleset->vchunk);
                goto err_chunk_get;
        }
 
@@ -1025,7 +1463,7 @@ mlxsw_sp_acl_tcam_mr_ruleset_del(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv)
 {
        struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = ruleset_priv;
 
-       mlxsw_sp_acl_tcam_chunk_put(mlxsw_sp, ruleset->chunk);
+       mlxsw_sp_acl_tcam_vchunk_put(mlxsw_sp, ruleset->vchunk);
        mlxsw_sp_acl_tcam_group_del(mlxsw_sp, &ruleset->group);
 }
 
@@ -1054,12 +1492,6 @@ mlxsw_sp_acl_tcam_mr_ruleset_group_id(void *ruleset_priv)
        return mlxsw_sp_acl_tcam_group_id(&ruleset->group);
 }
 
-static size_t mlxsw_sp_acl_tcam_mr_rule_priv_size(struct mlxsw_sp *mlxsw_sp)
-{
-       return sizeof(struct mlxsw_sp_acl_tcam_mr_rule) +
-              mlxsw_sp_acl_tcam_entry_priv_size(mlxsw_sp);
-}
-
 static int
 mlxsw_sp_acl_tcam_mr_rule_add(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv,
                              void *rule_priv,
@@ -1068,8 +1500,8 @@ mlxsw_sp_acl_tcam_mr_rule_add(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv,
        struct mlxsw_sp_acl_tcam_mr_ruleset *ruleset = ruleset_priv;
        struct mlxsw_sp_acl_tcam_mr_rule *rule = rule_priv;
 
-       return mlxsw_sp_acl_tcam_entry_add(mlxsw_sp, &ruleset->group,
-                                          &rule->entry, rulei);
+       return mlxsw_sp_acl_tcam_ventry_add(mlxsw_sp, &ruleset->group,
+                                          &rule->ventry, rulei);
 }
 
 static void
@@ -1077,7 +1509,7 @@ mlxsw_sp_acl_tcam_mr_rule_del(struct mlxsw_sp *mlxsw_sp, void *rule_priv)
 {
        struct mlxsw_sp_acl_tcam_mr_rule *rule = rule_priv;
 
-       mlxsw_sp_acl_tcam_entry_del(mlxsw_sp, &rule->entry);
+       mlxsw_sp_acl_tcam_ventry_del(mlxsw_sp, &rule->ventry);
 }
 
 static int
@@ -1087,8 +1519,8 @@ mlxsw_sp_acl_tcam_mr_rule_action_replace(struct mlxsw_sp *mlxsw_sp,
 {
        struct mlxsw_sp_acl_tcam_mr_rule *rule = rule_priv;
 
-       return mlxsw_sp_acl_tcam_entry_action_replace(mlxsw_sp, &rule->entry,
-                                                     rulei);
+       return mlxsw_sp_acl_tcam_ventry_action_replace(mlxsw_sp, &rule->ventry,
+                                                      rulei);
 }
 
 static int
@@ -1097,8 +1529,8 @@ mlxsw_sp_acl_tcam_mr_rule_activity_get(struct mlxsw_sp *mlxsw_sp,
 {
        struct mlxsw_sp_acl_tcam_mr_rule *rule = rule_priv;
 
-       return mlxsw_sp_acl_tcam_entry_activity_get(mlxsw_sp, &rule->entry,
-                                                   activity);
+       return mlxsw_sp_acl_tcam_ventry_activity_get(mlxsw_sp, &rule->ventry,
+                                                    activity);
 }
 
 static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_mr_ops = {
@@ -1108,7 +1540,7 @@ static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_mr_ops = {
        .ruleset_bind           = mlxsw_sp_acl_tcam_mr_ruleset_bind,
        .ruleset_unbind         = mlxsw_sp_acl_tcam_mr_ruleset_unbind,
        .ruleset_group_id       = mlxsw_sp_acl_tcam_mr_ruleset_group_id,
-       .rule_priv_size         = mlxsw_sp_acl_tcam_mr_rule_priv_size,
+       .rule_priv_size         = sizeof(struct mlxsw_sp_acl_tcam_mr_rule),
        .rule_add               = mlxsw_sp_acl_tcam_mr_rule_add,
        .rule_del               = mlxsw_sp_acl_tcam_mr_rule_del,
        .rule_action_replace    = mlxsw_sp_acl_tcam_mr_rule_action_replace,
index 10512b7..96bd42a 100644 (file)
@@ -17,6 +17,8 @@ struct mlxsw_sp_acl_tcam {
        unsigned long *used_groups;  /* bit array */
        unsigned int max_groups;
        unsigned int max_group_size;
+       struct list_head vregion_list;
+       u32 vregion_rehash_intrvl;   /* ms */
        unsigned long priv[0];
        /* priv has to be always the last item */
 };
@@ -26,6 +28,11 @@ int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp,
                           struct mlxsw_sp_acl_tcam *tcam);
 void mlxsw_sp_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp,
                            struct mlxsw_sp_acl_tcam *tcam);
+u32 mlxsw_sp_acl_tcam_vregion_rehash_intrvl_get(struct mlxsw_sp *mlxsw_sp,
+                                               struct mlxsw_sp_acl_tcam *tcam);
+int mlxsw_sp_acl_tcam_vregion_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp,
+                                               struct mlxsw_sp_acl_tcam *tcam,
+                                               u32 val);
 int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp,
                                   struct mlxsw_sp_acl_rule_info *rulei,
                                   u32 *priority, bool fillup_priority);
@@ -43,7 +50,7 @@ struct mlxsw_sp_acl_profile_ops {
                               struct mlxsw_sp_port *mlxsw_sp_port,
                               bool ingress);
        u16 (*ruleset_group_id)(void *ruleset_priv);
-       size_t (*rule_priv_size)(struct mlxsw_sp *mlxsw_sp);
+       size_t rule_priv_size;
        int (*rule_add)(struct mlxsw_sp *mlxsw_sp,
                        void *ruleset_priv, void *rule_priv,
                        struct mlxsw_sp_acl_rule_info *rulei);
@@ -67,11 +74,10 @@ mlxsw_sp_acl_tcam_profile_ops(struct mlxsw_sp *mlxsw_sp,
        (MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN * BITS_PER_BYTE)
 
 struct mlxsw_sp_acl_tcam_group;
+struct mlxsw_sp_acl_tcam_vregion;
 
 struct mlxsw_sp_acl_tcam_region {
-       struct list_head list; /* Member of a TCAM group */
-       struct list_head chunk_list; /* List of chunks under this region */
-       struct mlxsw_sp_acl_tcam_group *group;
+       struct mlxsw_sp_acl_tcam_vregion *vregion;
        enum mlxsw_reg_ptar_key_type key_type;
        u16 id; /* ACL ID and region ID - they are same */
        char tcam_region_info[MLXSW_REG_PXXX_TCAM_REGION_INFO_LEN];
@@ -161,9 +167,9 @@ struct mlxsw_sp_acl_atcam_region {
 };
 
 struct mlxsw_sp_acl_atcam_entry_ht_key {
-       char enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded key,
-                                                           * minus delta bits.
-                                                           */
+       char full_enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded
+                                                                * key.
+                                                                */
        u8 erp_id;
 };
 
@@ -175,7 +181,9 @@ struct mlxsw_sp_acl_atcam_entry {
        struct rhash_head ht_node;
        struct list_head list; /* Member in entries_list */
        struct mlxsw_sp_acl_atcam_entry_ht_key ht_key;
-       char full_enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded key */
+       char enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded key,
+                                                           * minus delta bits.
+                                                           */
        struct {
                u16 start;
                u8 mask;
@@ -205,6 +213,7 @@ mlxsw_sp_acl_atcam_region_init(struct mlxsw_sp *mlxsw_sp,
                               struct mlxsw_sp_acl_atcam *atcam,
                               struct mlxsw_sp_acl_atcam_region *aregion,
                               struct mlxsw_sp_acl_tcam_region *region,
+                              void *hints_priv,
                               const struct mlxsw_sp_acl_ctcam_region_ops *ops);
 void mlxsw_sp_acl_atcam_region_fini(struct mlxsw_sp_acl_atcam_region *aregion);
 void mlxsw_sp_acl_atcam_chunk_init(struct mlxsw_sp_acl_atcam_region *aregion,
@@ -228,6 +237,9 @@ int mlxsw_sp_acl_atcam_init(struct mlxsw_sp *mlxsw_sp,
                            struct mlxsw_sp_acl_atcam *atcam);
 void mlxsw_sp_acl_atcam_fini(struct mlxsw_sp *mlxsw_sp,
                             struct mlxsw_sp_acl_atcam *atcam);
+void *
+mlxsw_sp_acl_atcam_rehash_hints_get(struct mlxsw_sp_acl_atcam_region *aregion);
+void mlxsw_sp_acl_atcam_rehash_hints_put(void *hints_priv);
 
 struct mlxsw_sp_acl_erp_delta;
 
@@ -258,7 +270,11 @@ void mlxsw_sp_acl_erp_bf_remove(struct mlxsw_sp *mlxsw_sp,
                                struct mlxsw_sp_acl_atcam_region *aregion,
                                struct mlxsw_sp_acl_erp_mask *erp_mask,
                                struct mlxsw_sp_acl_atcam_entry *aentry);
-int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion);
+void *
+mlxsw_sp_acl_erp_rehash_hints_get(struct mlxsw_sp_acl_atcam_region *aregion);
+void mlxsw_sp_acl_erp_rehash_hints_put(void *hints_priv);
+int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion,
+                                void *hints_priv);
 void mlxsw_sp_acl_erp_region_fini(struct mlxsw_sp_acl_atcam_region *aregion);
 int mlxsw_sp_acl_erps_init(struct mlxsw_sp *mlxsw_sp,
                           struct mlxsw_sp_acl_atcam *atcam);
index 12c61e0..80066f4 100644 (file)
@@ -713,6 +713,7 @@ int mlxsw_sp_sb_pool_get(struct mlxsw_core *mlxsw_core,
        pool_info->pool_type = (enum devlink_sb_pool_type) dir;
        pool_info->size = mlxsw_sp_cells_bytes(mlxsw_sp, pr->size);
        pool_info->threshold_type = (enum devlink_sb_threshold_type) pr->mode;
+       pool_info->cell_size = mlxsw_sp->sb->cell_size;
        return 0;
 }
 
index ff07235..15f8044 100644 (file)
 static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
                                         struct mlxsw_sp_acl_block *block,
                                         struct mlxsw_sp_acl_rule_info *rulei,
-                                        struct tcf_exts *exts,
+                                        struct flow_action *flow_action,
                                         struct netlink_ext_ack *extack)
 {
-       const struct tc_action *a;
+       const struct flow_action_entry *act;
        int err, i;
 
-       if (!tcf_exts_has_actions(exts))
+       if (!flow_action_has_entries(flow_action))
                return 0;
 
        /* Count action is inserted first */
@@ -31,27 +31,31 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
        if (err)
                return err;
 
-       tcf_exts_for_each_action(i, a, exts) {
-               if (is_tcf_gact_ok(a)) {
+       flow_action_for_each(i, act, flow_action) {
+               switch (act->id) {
+               case FLOW_ACTION_ACCEPT:
                        err = mlxsw_sp_acl_rulei_act_terminate(rulei);
                        if (err) {
                                NL_SET_ERR_MSG_MOD(extack, "Cannot append terminate action");
                                return err;
                        }
-               } else if (is_tcf_gact_shot(a)) {
+                       break;
+               case FLOW_ACTION_DROP:
                        err = mlxsw_sp_acl_rulei_act_drop(rulei);
                        if (err) {
                                NL_SET_ERR_MSG_MOD(extack, "Cannot append drop action");
                                return err;
                        }
-               } else if (is_tcf_gact_trap(a)) {
+                       break;
+               case FLOW_ACTION_TRAP:
                        err = mlxsw_sp_acl_rulei_act_trap(rulei);
                        if (err) {
                                NL_SET_ERR_MSG_MOD(extack, "Cannot append trap action");
                                return err;
                        }
-               } else if (is_tcf_gact_goto_chain(a)) {
-                       u32 chain_index = tcf_gact_goto_chain_index(a);
+                       break;
+               case FLOW_ACTION_GOTO: {
+                       u32 chain_index = act->chain_index;
                        struct mlxsw_sp_acl_ruleset *ruleset;
                        u16 group_id;
 
@@ -67,7 +71,9 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
                                NL_SET_ERR_MSG_MOD(extack, "Cannot append jump action");
                                return err;
                        }
-               } else if (is_tcf_mirred_egress_redirect(a)) {
+                       }
+                       break;
+               case FLOW_ACTION_REDIRECT: {
                        struct net_device *out_dev;
                        struct mlxsw_sp_fid *fid;
                        u16 fid_index;
@@ -79,29 +85,33 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
                        if (err)
                                return err;
 
-                       out_dev = tcf_mirred_dev(a);
+                       out_dev = act->dev;
                        err = mlxsw_sp_acl_rulei_act_fwd(mlxsw_sp, rulei,
                                                         out_dev, extack);
                        if (err)
                                return err;
-               } else if (is_tcf_mirred_egress_mirror(a)) {
-                       struct net_device *out_dev = tcf_mirred_dev(a);
+                       }
+                       break;
+               case FLOW_ACTION_MIRRED: {
+                       struct net_device *out_dev = act->dev;
 
                        err = mlxsw_sp_acl_rulei_act_mirror(mlxsw_sp, rulei,
                                                            block, out_dev,
                                                            extack);
                        if (err)
                                return err;
-               } else if (is_tcf_vlan(a)) {
-                       u16 proto = be16_to_cpu(tcf_vlan_push_proto(a));
-                       u32 action = tcf_vlan_action(a);
-                       u8 prio = tcf_vlan_push_prio(a);
-                       u16 vid = tcf_vlan_push_vid(a);
+                       }
+                       break;
+               case FLOW_ACTION_VLAN_MANGLE: {
+                       u16 proto = be16_to_cpu(act->vlan.proto);
+                       u8 prio = act->vlan.prio;
+                       u16 vid = act->vlan.vid;
 
                        return mlxsw_sp_acl_rulei_act_vlan(mlxsw_sp, rulei,
-                                                          action, vid,
+                                                          act->id, vid,
                                                           proto, prio, extack);
-               } else {
+                       }
+               default:
                        NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
                        dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n");
                        return -EOPNOTSUPP;
@@ -113,59 +123,49 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
 static void mlxsw_sp_flower_parse_ipv4(struct mlxsw_sp_acl_rule_info *rulei,
                                       struct tc_cls_flower_offload *f)
 {
-       struct flow_dissector_key_ipv4_addrs *key =
-               skb_flow_dissector_target(f->dissector,
-                                         FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                         f->key);
-       struct flow_dissector_key_ipv4_addrs *mask =
-               skb_flow_dissector_target(f->dissector,
-                                         FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                         f->mask);
+       struct flow_match_ipv4_addrs match;
+
+       flow_rule_match_ipv4_addrs(f->rule, &match);
 
        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
-                                      (char *) &key->src,
-                                      (char *) &mask->src, 4);
+                                      (char *) &match.key->src,
+                                      (char *) &match.mask->src, 4);
        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
-                                      (char *) &key->dst,
-                                      (char *) &mask->dst, 4);
+                                      (char *) &match.key->dst,
+                                      (char *) &match.mask->dst, 4);
 }
 
 static void mlxsw_sp_flower_parse_ipv6(struct mlxsw_sp_acl_rule_info *rulei,
                                       struct tc_cls_flower_offload *f)
 {
-       struct flow_dissector_key_ipv6_addrs *key =
-               skb_flow_dissector_target(f->dissector,
-                                         FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                         f->key);
-       struct flow_dissector_key_ipv6_addrs *mask =
-               skb_flow_dissector_target(f->dissector,
-                                         FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                         f->mask);
+       struct flow_match_ipv6_addrs match;
+
+       flow_rule_match_ipv6_addrs(f->rule, &match);
 
        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_96_127,
-                                      &key->src.s6_addr[0x0],
-                                      &mask->src.s6_addr[0x0], 4);
+                                      &match.key->src.s6_addr[0x0],
+                                      &match.mask->src.s6_addr[0x0], 4);
        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_64_95,
-                                      &key->src.s6_addr[0x4],
-                                      &mask->src.s6_addr[0x4], 4);
+                                      &match.key->src.s6_addr[0x4],
+                                      &match.mask->src.s6_addr[0x4], 4);
        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_32_63,
-                                      &key->src.s6_addr[0x8],
-                                      &mask->src.s6_addr[0x8], 4);
+                                      &match.key->src.s6_addr[0x8],
+                                      &match.mask->src.s6_addr[0x8], 4);
        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
-                                      &key->src.s6_addr[0xC],
-                                      &mask->src.s6_addr[0xC], 4);
+                                      &match.key->src.s6_addr[0xC],
+                                      &match.mask->src.s6_addr[0xC], 4);
        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_96_127,
-                                      &key->dst.s6_addr[0x0],
-                                      &mask->dst.s6_addr[0x0], 4);
+                                      &match.key->dst.s6_addr[0x0],
+                                      &match.mask->dst.s6_addr[0x0], 4);
        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_64_95,
-                                      &key->dst.s6_addr[0x4],
-                                      &mask->dst.s6_addr[0x4], 4);
+                                      &match.key->dst.s6_addr[0x4],
+                                      &match.mask->dst.s6_addr[0x4], 4);
        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_32_63,
-                                      &key->dst.s6_addr[0x8],
-                                      &mask->dst.s6_addr[0x8], 4);
+                                      &match.key->dst.s6_addr[0x8],
+                                      &match.mask->dst.s6_addr[0x8], 4);
        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
-                                      &key->dst.s6_addr[0xC],
-                                      &mask->dst.s6_addr[0xC], 4);
+                                      &match.key->dst.s6_addr[0xC],
+                                      &match.mask->dst.s6_addr[0xC], 4);
 }
 
 static int mlxsw_sp_flower_parse_ports(struct mlxsw_sp *mlxsw_sp,
@@ -173,9 +173,10 @@ static int mlxsw_sp_flower_parse_ports(struct mlxsw_sp *mlxsw_sp,
                                       struct tc_cls_flower_offload *f,
                                       u8 ip_proto)
 {
-       struct flow_dissector_key_ports *key, *mask;
+       const struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
+       struct flow_match_ports match;
 
-       if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS))
+       if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS))
                return 0;
 
        if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) {
@@ -184,16 +185,13 @@ static int mlxsw_sp_flower_parse_ports(struct mlxsw_sp *mlxsw_sp,
                return -EINVAL;
        }
 
-       key = skb_flow_dissector_target(f->dissector,
-                                       FLOW_DISSECTOR_KEY_PORTS,
-                                       f->key);
-       mask = skb_flow_dissector_target(f->dissector,
-                                        FLOW_DISSECTOR_KEY_PORTS,
-                                        f->mask);
+       flow_rule_match_ports(rule, &match);
        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_DST_L4_PORT,
-                                      ntohs(key->dst), ntohs(mask->dst));
+                                      ntohs(match.key->dst),
+                                      ntohs(match.mask->dst));
        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_SRC_L4_PORT,
-                                      ntohs(key->src), ntohs(mask->src));
+                                      ntohs(match.key->src),
+                                      ntohs(match.mask->src));
        return 0;
 }
 
@@ -202,9 +200,10 @@ static int mlxsw_sp_flower_parse_tcp(struct mlxsw_sp *mlxsw_sp,
                                     struct tc_cls_flower_offload *f,
                                     u8 ip_proto)
 {
-       struct flow_dissector_key_tcp *key, *mask;
+       const struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
+       struct flow_match_tcp match;
 
-       if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_TCP))
+       if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP))
                return 0;
 
        if (ip_proto != IPPROTO_TCP) {
@@ -213,14 +212,11 @@ static int mlxsw_sp_flower_parse_tcp(struct mlxsw_sp *mlxsw_sp,
                return -EINVAL;
        }
 
-       key = skb_flow_dissector_target(f->dissector,
-                                       FLOW_DISSECTOR_KEY_TCP,
-                                       f->key);
-       mask = skb_flow_dissector_target(f->dissector,
-                                        FLOW_DISSECTOR_KEY_TCP,
-                                        f->mask);
+       flow_rule_match_tcp(rule, &match);
+
        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_TCP_FLAGS,
-                                      ntohs(key->flags), ntohs(mask->flags));
+                                      ntohs(match.key->flags),
+                                      ntohs(match.mask->flags));
        return 0;
 }
 
@@ -229,9 +225,10 @@ static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp,
                                    struct tc_cls_flower_offload *f,
                                    u16 n_proto)
 {
-       struct flow_dissector_key_ip *key, *mask;
+       const struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
+       struct flow_match_ip match;
 
-       if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IP))
+       if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP))
                return 0;
 
        if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6) {
@@ -240,20 +237,18 @@ static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp,
                return -EINVAL;
        }
 
-       key = skb_flow_dissector_target(f->dissector,
-                                       FLOW_DISSECTOR_KEY_IP,
-                                       f->key);
-       mask = skb_flow_dissector_target(f->dissector,
-                                        FLOW_DISSECTOR_KEY_IP,
-                                        f->mask);
+       flow_rule_match_ip(rule, &match);
+
        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_TTL_,
-                                      key->ttl, mask->ttl);
+                                      match.key->ttl, match.mask->ttl);
 
        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_ECN,
-                                      key->tos & 0x3, mask->tos & 0x3);
+                                      match.key->tos & 0x3,
+                                      match.mask->tos & 0x3);
 
        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_DSCP,
-                                      key->tos >> 6, mask->tos >> 6);
+                                      match.key->tos >> 6,
+                                      match.mask->tos >> 6);
 
        return 0;
 }
@@ -263,13 +258,15 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
                                 struct mlxsw_sp_acl_rule_info *rulei,
                                 struct tc_cls_flower_offload *f)
 {
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
+       struct flow_dissector *dissector = rule->match.dissector;
        u16 n_proto_mask = 0;
        u16 n_proto_key = 0;
        u16 addr_type = 0;
        u8 ip_proto = 0;
        int err;
 
-       if (f->dissector->used_keys &
+       if (dissector->used_keys &
            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
              BIT(FLOW_DISSECTOR_KEY_BASIC) |
              BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
@@ -286,25 +283,19 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
 
        mlxsw_sp_acl_rulei_priority(rulei, f->common.prio);
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
-               struct flow_dissector_key_control *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_CONTROL,
-                                                 f->key);
-               addr_type = key->addr_type;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+               struct flow_match_control match;
+
+               flow_rule_match_control(rule, &match);
+               addr_type = match.key->addr_type;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->key);
-               struct flow_dissector_key_basic *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->mask);
-               n_proto_key = ntohs(key->n_proto);
-               n_proto_mask = ntohs(mask->n_proto);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
+
+               flow_rule_match_basic(rule, &match);
+               n_proto_key = ntohs(match.key->n_proto);
+               n_proto_mask = ntohs(match.mask->n_proto);
 
                if (n_proto_key == ETH_P_ALL) {
                        n_proto_key = 0;
@@ -314,60 +305,53 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
                                               MLXSW_AFK_ELEMENT_ETHERTYPE,
                                               n_proto_key, n_proto_mask);
 
-               ip_proto = key->ip_proto;
+               ip_proto = match.key->ip_proto;
                mlxsw_sp_acl_rulei_keymask_u32(rulei,
                                               MLXSW_AFK_ELEMENT_IP_PROTO,
-                                              key->ip_proto, mask->ip_proto);
+                                              match.key->ip_proto,
+                                              match.mask->ip_proto);
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
-               struct flow_dissector_key_eth_addrs *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
-                                                 f->key);
-               struct flow_dissector_key_eth_addrs *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_ETH_ADDRS,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct flow_match_eth_addrs match;
 
+               flow_rule_match_eth_addrs(rule, &match);
                mlxsw_sp_acl_rulei_keymask_buf(rulei,
                                               MLXSW_AFK_ELEMENT_DMAC_32_47,
-                                              key->dst, mask->dst, 2);
+                                              match.key->dst,
+                                              match.mask->dst, 2);
                mlxsw_sp_acl_rulei_keymask_buf(rulei,
                                               MLXSW_AFK_ELEMENT_DMAC_0_31,
-                                              key->dst + 2, mask->dst + 2, 4);
+                                              match.key->dst + 2,
+                                              match.mask->dst + 2, 4);
                mlxsw_sp_acl_rulei_keymask_buf(rulei,
                                               MLXSW_AFK_ELEMENT_SMAC_32_47,
-                                              key->src, mask->src, 2);
+                                              match.key->src,
+                                              match.mask->src, 2);
                mlxsw_sp_acl_rulei_keymask_buf(rulei,
                                               MLXSW_AFK_ELEMENT_SMAC_0_31,
-                                              key->src + 2, mask->src + 2, 4);
+                                              match.key->src + 2,
+                                              match.mask->src + 2, 4);
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
-               struct flow_dissector_key_vlan *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_VLAN,
-                                                 f->key);
-               struct flow_dissector_key_vlan *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_VLAN,
-                                                 f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+               struct flow_match_vlan match;
 
+               flow_rule_match_vlan(rule, &match);
                if (mlxsw_sp_acl_block_is_egress_bound(block)) {
                        NL_SET_ERR_MSG_MOD(f->common.extack, "vlan_id key is not supported on egress");
                        return -EOPNOTSUPP;
                }
-               if (mask->vlan_id != 0)
+               if (match.mask->vlan_id != 0)
                        mlxsw_sp_acl_rulei_keymask_u32(rulei,
                                                       MLXSW_AFK_ELEMENT_VID,
-                                                      key->vlan_id,
-                                                      mask->vlan_id);
-               if (mask->vlan_priority != 0)
+                                                      match.key->vlan_id,
+                                                      match.mask->vlan_id);
+               if (match.mask->vlan_priority != 0)
                        mlxsw_sp_acl_rulei_keymask_u32(rulei,
                                                       MLXSW_AFK_ELEMENT_PCP,
-                                                      key->vlan_priority,
-                                                      mask->vlan_priority);
+                                                      match.key->vlan_priority,
+                                                      match.mask->vlan_priority);
        }
 
        if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS)
@@ -387,7 +371,8 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
        if (err)
                return err;
 
-       return mlxsw_sp_flower_parse_actions(mlxsw_sp, block, rulei, f->exts,
+       return mlxsw_sp_flower_parse_actions(mlxsw_sp, block, rulei,
+                                            &f->rule->action,
                                             f->common.extack);
 }
 
@@ -486,7 +471,7 @@ int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_rule_get_stats;
 
-       tcf_exts_stats_update(f->exts, bytes, packets, lastuse);
+       flow_stats_update(&f->stats, bytes, packets, lastuse);
 
        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
        return 0;
index 230e1f6..52fed8c 100644 (file)
@@ -364,6 +364,7 @@ enum mlxsw_sp_fib_entry_type {
        MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
        MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
        MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
+       MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE,
 
        /* This is a special case of local delivery, where a packet should be
         * decapsulated on reception. Note that there is no corresponding ENCAP,
@@ -3813,13 +3814,11 @@ mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
        struct mlxsw_sp_nexthop_group *nh_grp;
        struct mlxsw_sp_nexthop *nh;
        struct fib_nh *fib_nh;
-       size_t alloc_size;
        int i;
        int err;
 
-       alloc_size = sizeof(*nh_grp) +
-                    fi->fib_nhs * sizeof(struct mlxsw_sp_nexthop);
-       nh_grp = kzalloc(alloc_size, GFP_KERNEL);
+       nh_grp = kzalloc(struct_size(nh_grp, nexthops, fi->fib_nhs),
+                        GFP_KERNEL);
        if (!nh_grp)
                return ERR_PTR(-ENOMEM);
        nh_grp->priv = fi;
@@ -3928,6 +3927,7 @@ mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry)
                return !!nh_group->adj_index_valid;
        case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
                return !!nh_group->nh_rif;
+       case MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE:
        case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
        case MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP:
                return true;
@@ -3963,6 +3963,7 @@ mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
        int i;
 
        if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL ||
+           fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE ||
            fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP ||
            fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP) {
                nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD;
@@ -4004,7 +4005,8 @@ mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry)
        fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry,
                                  common);
 
-       if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) {
+       if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL ||
+           fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE) {
                list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6,
                                 list)->rt->fib6_nh.nh_flags |= RTNH_F_OFFLOAD;
                return;
@@ -4172,6 +4174,19 @@ static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp,
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
 }
 
+static int mlxsw_sp_fib_entry_op_blackhole(struct mlxsw_sp *mlxsw_sp,
+                                          struct mlxsw_sp_fib_entry *fib_entry,
+                                          enum mlxsw_reg_ralue_op op)
+{
+       enum mlxsw_reg_ralue_trap_action trap_action;
+       char ralue_pl[MLXSW_REG_RALUE_LEN];
+
+       trap_action = MLXSW_REG_RALUE_TRAP_ACTION_DISCARD_ERROR;
+       mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op);
+       mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, 0, 0);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
+}
+
 static int
 mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp,
                                 struct mlxsw_sp_fib_entry *fib_entry,
@@ -4211,6 +4226,8 @@ static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
                return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op);
        case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
                return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op);
+       case MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE:
+               return mlxsw_sp_fib_entry_op_blackhole(mlxsw_sp, fib_entry, op);
        case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP:
                return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp,
                                                        fib_entry, op);
@@ -4279,8 +4296,10 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp,
        case RTN_BROADCAST:
                fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
                return 0;
+       case RTN_BLACKHOLE:
+               fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE;
+               return 0;
        case RTN_UNREACHABLE: /* fall through */
-       case RTN_BLACKHOLE: /* fall through */
        case RTN_PROHIBIT:
                /* Packets hitting these routes need to be trapped, but
                 * can do so with a lower priority than packets directed
@@ -5045,13 +5064,11 @@ mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp,
        struct mlxsw_sp_nexthop_group *nh_grp;
        struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
        struct mlxsw_sp_nexthop *nh;
-       size_t alloc_size;
        int i = 0;
        int err;
 
-       alloc_size = sizeof(*nh_grp) +
-                    fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop);
-       nh_grp = kzalloc(alloc_size, GFP_KERNEL);
+       nh_grp = kzalloc(struct_size(nh_grp, nexthops, fib6_entry->nrt6),
+                        GFP_KERNEL);
        if (!nh_grp)
                return ERR_PTR(-ENOMEM);
        INIT_LIST_HEAD(&nh_grp->fib_list);
@@ -5229,6 +5246,8 @@ static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp,
         */
        if (rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST))
                fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
+       else if (rt->fib6_type == RTN_BLACKHOLE)
+               fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE;
        else if (rt->fib6_flags & RTF_REJECT)
                fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
        else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt))
@@ -6123,7 +6142,7 @@ static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
 
        mlxsw_reg_ritr_rif_pack(ritr_pl, rif);
        err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
-       if (WARN_ON_ONCE(err))
+       if (err)
                return err;
 
        mlxsw_reg_ritr_enable_set(ritr_pl, false);
index a4a9fe9..1f492b7 100644 (file)
@@ -431,35 +431,10 @@ static void mlxsw_sp_bridge_vlan_put(struct mlxsw_sp_bridge_vlan *bridge_vlan)
                mlxsw_sp_bridge_vlan_destroy(bridge_vlan);
 }
 
-static void mlxsw_sp_port_bridge_flags_get(struct mlxsw_sp_bridge *bridge,
-                                          struct net_device *dev,
-                                          unsigned long *brport_flags)
-{
-       struct mlxsw_sp_bridge_port *bridge_port;
-
-       bridge_port = mlxsw_sp_bridge_port_find(bridge, dev);
-       if (WARN_ON(!bridge_port))
-               return;
-
-       memcpy(brport_flags, &bridge_port->flags, sizeof(*brport_flags));
-}
-
 static int mlxsw_sp_port_attr_get(struct net_device *dev,
                                  struct switchdev_attr *attr)
 {
-       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-
        switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = sizeof(mlxsw_sp->base_mac);
-               memcpy(&attr->u.ppid.id, &mlxsw_sp->base_mac,
-                      attr->u.ppid.id_len);
-               break;
-       case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
-               mlxsw_sp_port_bridge_flags_get(mlxsw_sp->bridge, attr->orig_dev,
-                                              &attr->u.brport_flags);
-               break;
        case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
                attr->u.brport_flags_support = BR_LEARNING | BR_FLOOD |
                                               BR_MCAST_FLOOD;
index 2d4f213..533fe62 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/device.h>
 #include <linux/skbuff.h>
 #include <linux/if_vlan.h>
-#include <net/switchdev.h>
 
 #include "pci.h"
 #include "core.h"
@@ -390,6 +389,18 @@ static int mlxsw_sx_port_get_phys_port_name(struct net_device *dev, char *name,
                                                  name, len);
 }
 
+static int mlxsw_sx_port_get_port_parent_id(struct net_device *dev,
+                                           struct netdev_phys_item_id *ppid)
+{
+       struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
+       struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
+
+       ppid->id_len = sizeof(mlxsw_sx->hw_id);
+       memcpy(&ppid->id, &mlxsw_sx->hw_id, ppid->id_len);
+
+       return 0;
+}
+
 static const struct net_device_ops mlxsw_sx_port_netdev_ops = {
        .ndo_open               = mlxsw_sx_port_open,
        .ndo_stop               = mlxsw_sx_port_stop,
@@ -397,6 +408,7 @@ static const struct net_device_ops mlxsw_sx_port_netdev_ops = {
        .ndo_change_mtu         = mlxsw_sx_port_change_mtu,
        .ndo_get_stats64        = mlxsw_sx_port_get_stats64,
        .ndo_get_phys_port_name = mlxsw_sx_port_get_phys_port_name,
+       .ndo_get_port_parent_id = mlxsw_sx_port_get_port_parent_id,
 };
 
 static void mlxsw_sx_port_get_drvinfo(struct net_device *dev,
@@ -901,28 +913,6 @@ static const struct ethtool_ops mlxsw_sx_port_ethtool_ops = {
        .set_link_ksettings     = mlxsw_sx_port_set_link_ksettings,
 };
 
-static int mlxsw_sx_port_attr_get(struct net_device *dev,
-                                 struct switchdev_attr *attr)
-{
-       struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
-       struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
-
-       switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = sizeof(mlxsw_sx->hw_id);
-               memcpy(&attr->u.ppid.id, &mlxsw_sx->hw_id, attr->u.ppid.id_len);
-               break;
-       default:
-               return -EOPNOTSUPP;
-       }
-
-       return 0;
-}
-
-static const struct switchdev_ops mlxsw_sx_port_switchdev_ops = {
-       .switchdev_port_attr_get        = mlxsw_sx_port_attr_get,
-};
-
 static int mlxsw_sx_hw_id_get(struct mlxsw_sx *mlxsw_sx)
 {
        char spad_pl[MLXSW_REG_SPAD_LEN] = {0};
@@ -1034,7 +1024,6 @@ static int __mlxsw_sx_port_eth_create(struct mlxsw_sx *mlxsw_sx, u8 local_port,
 
        dev->netdev_ops = &mlxsw_sx_port_netdev_ops;
        dev->ethtool_ops = &mlxsw_sx_port_ethtool_ops;
-       dev->switchdev_ops = &mlxsw_sx_port_switchdev_ops;
 
        err = mlxsw_sx_port_dev_addr_get(mlxsw_sx_port);
        if (err) {
index b881f5d..6006d47 100644 (file)
@@ -391,7 +391,7 @@ ks8695_tx_irq(int irq, void *dev_id)
                                         ksp->tx_buffers[buff_n].dma_ptr,
                                         ksp->tx_buffers[buff_n].length,
                                         DMA_TO_DEVICE);
-                       dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+                       dev_consume_skb_irq(ksp->tx_buffers[buff_n].skb);
                        ksp->tx_buffers[buff_n].skb = NULL;
                        ksp->tx_ring_used--;
                }
index b34055a..e165175 100644 (file)
@@ -81,11 +81,13 @@ static void moxart_mac_free_memory(struct net_device *ndev)
                                 priv->rx_buf_size, DMA_FROM_DEVICE);
 
        if (priv->tx_desc_base)
-               dma_free_coherent(NULL, TX_REG_DESC_SIZE * TX_DESC_NUM,
+               dma_free_coherent(&priv->pdev->dev,
+                                 TX_REG_DESC_SIZE * TX_DESC_NUM,
                                  priv->tx_desc_base, priv->tx_base);
 
        if (priv->rx_desc_base)
-               dma_free_coherent(NULL, RX_REG_DESC_SIZE * RX_DESC_NUM,
+               dma_free_coherent(&priv->pdev->dev,
+                                 RX_REG_DESC_SIZE * RX_DESC_NUM,
                                  priv->rx_desc_base, priv->rx_base);
 
        kfree(priv->tx_buf_base);
@@ -298,7 +300,7 @@ static void moxart_tx_finished(struct net_device *ndev)
                ndev->stats.tx_packets++;
                ndev->stats.tx_bytes += priv->tx_skb[tx_tail]->len;
 
-               dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
+               dev_consume_skb_irq(priv->tx_skb[tx_tail]);
                priv->tx_skb[tx_tail] = NULL;
 
                tx_tail = TX_NEXT(tx_tail);
@@ -476,6 +478,7 @@ static int moxart_mac_probe(struct platform_device *pdev)
 
        priv = netdev_priv(ndev);
        priv->ndev = ndev;
+       priv->pdev = pdev;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        ndev->base_addr = res->start;
@@ -491,7 +494,7 @@ static int moxart_mac_probe(struct platform_device *pdev)
        priv->tx_buf_size = TX_BUF_SIZE;
        priv->rx_buf_size = RX_BUF_SIZE;
 
-       priv->tx_desc_base = dma_alloc_coherent(NULL, TX_REG_DESC_SIZE *
+       priv->tx_desc_base = dma_alloc_coherent(&pdev->dev, TX_REG_DESC_SIZE *
                                                TX_DESC_NUM, &priv->tx_base,
                                                GFP_DMA | GFP_KERNEL);
        if (!priv->tx_desc_base) {
@@ -499,7 +502,7 @@ static int moxart_mac_probe(struct platform_device *pdev)
                goto init_fail;
        }
 
-       priv->rx_desc_base = dma_alloc_coherent(NULL, RX_REG_DESC_SIZE *
+       priv->rx_desc_base = dma_alloc_coherent(&pdev->dev, RX_REG_DESC_SIZE *
                                                RX_DESC_NUM, &priv->rx_base,
                                                GFP_DMA | GFP_KERNEL);
        if (!priv->rx_desc_base) {
index bee608b..bf4c302 100644 (file)
 #define LINK_STATUS            0x4
 
 struct moxart_mac_priv_t {
+       struct platform_device *pdev;
        void __iomem *base;
        unsigned int reg_maccr;
        unsigned int reg_imr;
index c6a575e..195306d 100644 (file)
@@ -916,6 +916,18 @@ 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 *ocelot_port = netdev_priv(dev);
+       struct ocelot *ocelot = ocelot_port->ocelot;
+
+       ppid->id_len = sizeof(ocelot->base_mac);
+       memcpy(&ppid->id, &ocelot->base_mac, ppid->id_len);
+
+       return 0;
+}
+
 static const struct net_device_ops ocelot_port_netdev_ops = {
        .ndo_open                       = ocelot_port_open,
        .ndo_stop                       = ocelot_port_stop,
@@ -930,6 +942,7 @@ 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,
 };
 
 static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data)
@@ -1013,25 +1026,6 @@ static const struct ethtool_ops ocelot_ethtool_ops = {
        .set_link_ksettings     = phy_ethtool_set_link_ksettings,
 };
 
-static int ocelot_port_attr_get(struct net_device *dev,
-                               struct switchdev_attr *attr)
-{
-       struct ocelot_port *ocelot_port = netdev_priv(dev);
-       struct ocelot *ocelot = ocelot_port->ocelot;
-
-       switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = sizeof(ocelot->base_mac);
-               memcpy(&attr->u.ppid.id, &ocelot->base_mac,
-                      attr->u.ppid.id_len);
-               break;
-       default:
-               return -EOPNOTSUPP;
-       }
-
-       return 0;
-}
-
 static int ocelot_port_attr_stp_state_set(struct ocelot_port *ocelot_port,
                                          struct switchdev_trans *trans,
                                          u8 state)
@@ -1331,7 +1325,6 @@ static int ocelot_port_obj_del(struct net_device *dev,
 }
 
 static const struct switchdev_ops ocelot_port_switchdev_ops = {
-       .switchdev_port_attr_get        = ocelot_port_attr_get,
        .switchdev_port_attr_set        = ocelot_port_attr_set,
 };
 
index 19ce0e6..e0340f7 100644 (file)
@@ -1408,7 +1408,7 @@ myri10ge_tx_done(struct myri10ge_slice_state *ss, int mcp_index)
                if (skb) {
                        ss->stats.tx_bytes += skb->len;
                        ss->stats.tx_packets++;
-                       dev_kfree_skb_irq(skb);
+                       dev_consume_skb_irq(skb);
                        if (len)
                                pci_unmap_single(pdev,
                                                 dma_unmap_addr(&tx->info[idx],
index b9a1a9f..1a2634c 100644 (file)
@@ -2173,7 +2173,7 @@ static void netdev_tx_done(struct net_device *dev)
                                        np->tx_skbuff[entry]->len,
                                        PCI_DMA_TODEVICE);
                /* Free the original skb. */
-               dev_kfree_skb_irq(np->tx_skbuff[entry]);
+               dev_consume_skb_irq(np->tx_skbuff[entry]);
                np->tx_skbuff[entry] = NULL;
        }
        if (netif_queue_stopped(dev) &&
index 958fced..000009e 100644 (file)
@@ -1003,7 +1003,7 @@ static void do_tx_done(struct net_device *ndev)
                                        addr,
                                        len,
                                        PCI_DMA_TODEVICE);
-                       dev_kfree_skb_irq(skb);
+                       dev_consume_skb_irq(skb);
                        atomic_dec(&dev->nr_tx_skbs);
                } else
                        pci_unmap_page(dev->pci_dev,
index c805dcb..aaec009 100644 (file)
@@ -328,7 +328,7 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id)
                                }
 
                                /* We must free the original skb */
-                               dev_kfree_skb_irq(lp->tx_skb[entry]);
+                               dev_consume_skb_irq(lp->tx_skb[entry]);
                                lp->tx_skb[entry] = NULL;
                                /* and unmap DMA buffer */
                                dma_unmap_single(lp->device, lp->tx_laddr[entry], lp->tx_len[entry], DMA_TO_DEVICE);
index 82be900..feda964 100644 (file)
@@ -3055,7 +3055,7 @@ static void tx_intr_handler(struct fifo_info *fifo_data)
 
                /* Updating the statistics block */
                swstats->mem_freed += skb->truesize;
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
 
                get_info.offset++;
                if (get_info.offset == get_info.fifo_len + 1)
index 0da7393..b877ace 100644 (file)
@@ -114,7 +114,7 @@ static inline void VXGE_COMPLETE_VPATH_TX(struct vxge_fifo *fifo)
 
                /* free SKBs */
                for (temp = completed; temp != skb_ptr; temp++)
-                       dev_kfree_skb_irq(*temp);
+                       dev_consume_skb_irq(*temp);
        } while (more);
 }
 
index aa3a209..4d9d380 100644 (file)
@@ -1967,6 +1967,9 @@ static int neg_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
  */
 static int __shl_imm64(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt)
 {
+       if (!shift_amt)
+               return 0;
+
        if (shift_amt < 32) {
                emit_shf(nfp_prog, reg_both(dst + 1), reg_a(dst + 1),
                         SHF_OP_NONE, reg_b(dst), SHF_SC_R_DSHF,
@@ -2079,6 +2082,9 @@ static int shl_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
  */
 static int __shr_imm64(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt)
 {
+       if (!shift_amt)
+               return 0;
+
        if (shift_amt < 32) {
                emit_shf(nfp_prog, reg_both(dst), reg_a(dst + 1), SHF_OP_NONE,
                         reg_b(dst), SHF_SC_R_DSHF, shift_amt);
@@ -2180,6 +2186,9 @@ static int shr_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
  */
 static int __ashr_imm64(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt)
 {
+       if (!shift_amt)
+               return 0;
+
        if (shift_amt < 32) {
                emit_shf(nfp_prog, reg_both(dst), reg_a(dst + 1), SHF_OP_NONE,
                         reg_b(dst), SHF_SC_R_DSHF, shift_amt);
@@ -2388,10 +2397,13 @@ static int neg_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 
 static int __ashr_imm(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt)
 {
-       /* Set signedness bit (MSB of result). */
-       emit_alu(nfp_prog, reg_none(), reg_a(dst), ALU_OP_OR, reg_imm(0));
-       emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_ASHR, reg_b(dst),
-                SHF_SC_R_SHF, shift_amt);
+       if (shift_amt) {
+               /* Set signedness bit (MSB of result). */
+               emit_alu(nfp_prog, reg_none(), reg_a(dst), ALU_OP_OR,
+                        reg_imm(0));
+               emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_ASHR,
+                        reg_b(dst), SHF_SC_R_SHF, shift_amt);
+       }
        wrp_immed(nfp_prog, reg_both(dst + 1), 0);
 
        return 0;
@@ -2429,18 +2441,75 @@ static int ashr_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
        return __ashr_imm(nfp_prog, dst, insn->imm);
 }
 
+static int __shr_imm(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt)
+{
+       if (shift_amt)
+               emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE,
+                        reg_b(dst), SHF_SC_R_SHF, shift_amt);
+       wrp_immed(nfp_prog, reg_both(dst + 1), 0);
+       return 0;
+}
+
+static int shr_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+       const struct bpf_insn *insn = &meta->insn;
+       u8 dst = insn->dst_reg * 2;
+
+       return __shr_imm(nfp_prog, dst, insn->imm);
+}
+
+static int shr_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+       const struct bpf_insn *insn = &meta->insn;
+       u64 umin, umax;
+       u8 dst, src;
+
+       dst = insn->dst_reg * 2;
+       umin = meta->umin_src;
+       umax = meta->umax_src;
+       if (umin == umax)
+               return __shr_imm(nfp_prog, dst, umin);
+
+       src = insn->src_reg * 2;
+       emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0));
+       emit_shf_indir(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE,
+                      reg_b(dst), SHF_SC_R_SHF);
+       wrp_immed(nfp_prog, reg_both(dst + 1), 0);
+       return 0;
+}
+
+static int __shl_imm(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt)
+{
+       if (shift_amt)
+               emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE,
+                        reg_b(dst), SHF_SC_L_SHF, shift_amt);
+       wrp_immed(nfp_prog, reg_both(dst + 1), 0);
+       return 0;
+}
+
 static int shl_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 {
        const struct bpf_insn *insn = &meta->insn;
+       u8 dst = insn->dst_reg * 2;
 
-       if (!insn->imm)
-               return 1; /* TODO: zero shift means indirect */
+       return __shl_imm(nfp_prog, dst, insn->imm);
+}
 
-       emit_shf(nfp_prog, reg_both(insn->dst_reg * 2),
-                reg_none(), SHF_OP_NONE, reg_b(insn->dst_reg * 2),
-                SHF_SC_L_SHF, insn->imm);
-       wrp_immed(nfp_prog, reg_both(insn->dst_reg * 2 + 1), 0);
+static int shl_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+       const struct bpf_insn *insn = &meta->insn;
+       u64 umin, umax;
+       u8 dst, src;
 
+       dst = insn->dst_reg * 2;
+       umin = meta->umin_src;
+       umax = meta->umax_src;
+       if (umin == umax)
+               return __shl_imm(nfp_prog, dst, umin);
+
+       src = insn->src_reg * 2;
+       shl_reg64_lt32_low(nfp_prog, dst, src);
+       wrp_immed(nfp_prog, reg_both(dst + 1), 0);
        return 0;
 }
 
@@ -3350,7 +3419,10 @@ static const instr_cb_t instr_cb[256] = {
        [BPF_ALU | BPF_DIV | BPF_X] =   div_reg,
        [BPF_ALU | BPF_DIV | BPF_K] =   div_imm,
        [BPF_ALU | BPF_NEG] =           neg_reg,
+       [BPF_ALU | BPF_LSH | BPF_X] =   shl_reg,
        [BPF_ALU | BPF_LSH | BPF_K] =   shl_imm,
+       [BPF_ALU | BPF_RSH | BPF_X] =   shr_reg,
+       [BPF_ALU | BPF_RSH | BPF_K] =   shr_imm,
        [BPF_ALU | BPF_ARSH | BPF_X] =  ashr_reg,
        [BPF_ALU | BPF_ARSH | BPF_K] =  ashr_imm,
        [BPF_ALU | BPF_END | BPF_X] =   end_reg32,
index dccae03..275de9f 100644 (file)
@@ -465,7 +465,7 @@ static int nfp_bpf_init(struct nfp_app *app)
                app->ctrl_mtu = nfp_bpf_ctrl_cmsg_mtu(bpf);
        }
 
-       bpf->bpf_dev = bpf_offload_dev_create(&nfp_bpf_dev_ops);
+       bpf->bpf_dev = bpf_offload_dev_create(&nfp_bpf_dev_ops, bpf);
        err = PTR_ERR_OR_ZERO(bpf->bpf_dev);
        if (err)
                goto err_free_neutral_maps;
index 55c7dbf..15dce97 100644 (file)
@@ -185,8 +185,6 @@ static void nfp_prog_free(struct nfp_prog *nfp_prog)
 
 static int nfp_bpf_verifier_prep(struct bpf_prog *prog)
 {
-       struct nfp_net *nn = netdev_priv(prog->aux->offload->netdev);
-       struct nfp_app *app = nn->app;
        struct nfp_prog *nfp_prog;
        int ret;
 
@@ -197,7 +195,7 @@ static int nfp_bpf_verifier_prep(struct bpf_prog *prog)
 
        INIT_LIST_HEAD(&nfp_prog->insns);
        nfp_prog->type = prog->type;
-       nfp_prog->bpf = app->priv;
+       nfp_prog->bpf = bpf_offload_dev_priv(prog->aux->offload->offdev);
 
        ret = nfp_prog_prepare(nfp_prog, prog->insnsi, prog->len);
        if (ret)
index 8d54b36..eeda4ed 100644 (file)
@@ -3,7 +3,6 @@
 
 #include <linux/bitfield.h>
 #include <net/pkt_cls.h>
-#include <net/switchdev.h>
 #include <net/tc_act/tc_csum.h>
 #include <net/tc_act/tc_gact.h>
 #include <net/tc_act/tc_mirred.h>
@@ -37,7 +36,7 @@ static void nfp_fl_pop_vlan(struct nfp_fl_pop_vlan *pop_vlan)
 
 static void
 nfp_fl_push_vlan(struct nfp_fl_push_vlan *push_vlan,
-                const struct tc_action *action)
+                const struct flow_action_entry *act)
 {
        size_t act_size = sizeof(struct nfp_fl_push_vlan);
        u16 tmp_push_vlan_tci;
@@ -45,17 +44,17 @@ nfp_fl_push_vlan(struct nfp_fl_push_vlan *push_vlan,
        push_vlan->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_VLAN;
        push_vlan->head.len_lw = act_size >> NFP_FL_LW_SIZ;
        push_vlan->reserved = 0;
-       push_vlan->vlan_tpid = tcf_vlan_push_proto(action);
+       push_vlan->vlan_tpid = act->vlan.proto;
 
        tmp_push_vlan_tci =
-               FIELD_PREP(NFP_FL_PUSH_VLAN_PRIO, tcf_vlan_push_prio(action)) |
-               FIELD_PREP(NFP_FL_PUSH_VLAN_VID, tcf_vlan_push_vid(action)) |
+               FIELD_PREP(NFP_FL_PUSH_VLAN_PRIO, act->vlan.prio) |
+               FIELD_PREP(NFP_FL_PUSH_VLAN_VID, act->vlan.vid) |
                NFP_FL_PUSH_VLAN_CFI;
        push_vlan->vlan_tci = cpu_to_be16(tmp_push_vlan_tci);
 }
 
 static int
-nfp_fl_pre_lag(struct nfp_app *app, const struct tc_action *action,
+nfp_fl_pre_lag(struct nfp_app *app, const struct flow_action_entry *act,
               struct nfp_fl_payload *nfp_flow, int act_len)
 {
        size_t act_size = sizeof(struct nfp_fl_pre_lag);
@@ -63,7 +62,7 @@ nfp_fl_pre_lag(struct nfp_app *app, const struct tc_action *action,
        struct net_device *out_dev;
        int err;
 
-       out_dev = tcf_mirred_dev(action);
+       out_dev = act->dev;
        if (!out_dev || !netif_is_lag_master(out_dev))
                return 0;
 
@@ -92,7 +91,8 @@ nfp_fl_pre_lag(struct nfp_app *app, const struct tc_action *action,
 
 static int
 nfp_fl_output(struct nfp_app *app, struct nfp_fl_output *output,
-             const struct tc_action *action, struct nfp_fl_payload *nfp_flow,
+             const struct flow_action_entry *act,
+             struct nfp_fl_payload *nfp_flow,
              bool last, struct net_device *in_dev,
              enum nfp_flower_tun_type tun_type, int *tun_out_cnt)
 {
@@ -104,7 +104,7 @@ nfp_fl_output(struct nfp_app *app, struct nfp_fl_output *output,
        output->head.jump_id = NFP_FL_ACTION_OPCODE_OUTPUT;
        output->head.len_lw = act_size >> NFP_FL_LW_SIZ;
 
-       out_dev = tcf_mirred_dev(action);
+       out_dev = act->dev;
        if (!out_dev)
                return -EOPNOTSUPP;
 
@@ -137,7 +137,7 @@ nfp_fl_output(struct nfp_app *app, struct nfp_fl_output *output,
 
                if (nfp_netdev_is_nfp_repr(in_dev)) {
                        /* Confirm ingress and egress are on same device. */
-                       if (!switchdev_port_same_parent_id(in_dev, out_dev))
+                       if (!netdev_port_same_parent_id(in_dev, out_dev))
                                return -EOPNOTSUPP;
                }
 
@@ -155,9 +155,9 @@ nfp_fl_output(struct nfp_app *app, struct nfp_fl_output *output,
 
 static enum nfp_flower_tun_type
 nfp_fl_get_tun_from_act_l4_port(struct nfp_app *app,
-                               const struct tc_action *action)
+                               const struct flow_action_entry *act)
 {
-       struct ip_tunnel_info *tun = tcf_tunnel_info(action);
+       const struct ip_tunnel_info *tun = act->tunnel;
        struct nfp_flower_priv *priv = app->priv;
 
        switch (tun->key.tp_dst) {
@@ -195,9 +195,9 @@ static struct nfp_fl_pre_tunnel *nfp_fl_pre_tunnel(char *act_data, int act_len)
 
 static int
 nfp_fl_push_geneve_options(struct nfp_fl_payload *nfp_fl, int *list_len,
-                          const struct tc_action *action)
+                          const struct flow_action_entry *act)
 {
-       struct ip_tunnel_info *ip_tun = tcf_tunnel_info(action);
+       struct ip_tunnel_info *ip_tun = (struct ip_tunnel_info *)act->tunnel;
        int opt_len, opt_cnt, act_start, tot_push_len;
        u8 *src = ip_tunnel_info_opts(ip_tun);
 
@@ -259,13 +259,13 @@ nfp_fl_push_geneve_options(struct nfp_fl_payload *nfp_fl, int *list_len,
 static int
 nfp_fl_set_ipv4_udp_tun(struct nfp_app *app,
                        struct nfp_fl_set_ipv4_udp_tun *set_tun,
-                       const struct tc_action *action,
+                       const struct flow_action_entry *act,
                        struct nfp_fl_pre_tunnel *pre_tun,
                        enum nfp_flower_tun_type tun_type,
                        struct net_device *netdev)
 {
        size_t act_size = sizeof(struct nfp_fl_set_ipv4_udp_tun);
-       struct ip_tunnel_info *ip_tun = tcf_tunnel_info(action);
+       const struct ip_tunnel_info *ip_tun = act->tunnel;
        struct nfp_flower_priv *priv = app->priv;
        u32 tmp_set_ip_tun_type_index = 0;
        /* Currently support one pre-tunnel so index is always 0. */
@@ -345,7 +345,7 @@ static void nfp_fl_set_helper32(u32 value, u32 mask, u8 *p_exact, u8 *p_mask)
 }
 
 static int
-nfp_fl_set_eth(const struct tc_action *action, int idx, u32 off,
+nfp_fl_set_eth(const struct flow_action_entry *act, u32 off,
               struct nfp_fl_set_eth *set_eth)
 {
        u32 exact, mask;
@@ -353,8 +353,8 @@ nfp_fl_set_eth(const struct tc_action *action, int idx, u32 off,
        if (off + 4 > ETH_ALEN * 2)
                return -EOPNOTSUPP;
 
-       mask = ~tcf_pedit_mask(action, idx);
-       exact = tcf_pedit_val(action, idx);
+       mask = ~act->mangle.mask;
+       exact = act->mangle.val;
 
        if (exact & ~mask)
                return -EOPNOTSUPP;
@@ -376,7 +376,7 @@ struct ipv4_ttl_word {
 };
 
 static int
-nfp_fl_set_ip4(const struct tc_action *action, int idx, u32 off,
+nfp_fl_set_ip4(const struct flow_action_entry *act, u32 off,
               struct nfp_fl_set_ip4_addrs *set_ip_addr,
               struct nfp_fl_set_ip4_ttl_tos *set_ip_ttl_tos)
 {
@@ -387,8 +387,8 @@ nfp_fl_set_ip4(const struct tc_action *action, int idx, u32 off,
        __be32 exact, mask;
 
        /* We are expecting tcf_pedit to return a big endian value */
-       mask = (__force __be32)~tcf_pedit_mask(action, idx);
-       exact = (__force __be32)tcf_pedit_val(action, idx);
+       mask = (__force __be32)~act->mangle.mask;
+       exact = (__force __be32)act->mangle.val;
 
        if (exact & ~mask)
                return -EOPNOTSUPP;
@@ -505,7 +505,7 @@ nfp_fl_set_ip6_hop_limit_flow_label(u32 off, __be32 exact, __be32 mask,
 }
 
 static int
-nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off,
+nfp_fl_set_ip6(const struct flow_action_entry *act, u32 off,
               struct nfp_fl_set_ipv6_addr *ip_dst,
               struct nfp_fl_set_ipv6_addr *ip_src,
               struct nfp_fl_set_ipv6_tc_hl_fl *ip_hl_fl)
@@ -515,8 +515,8 @@ nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off,
        u8 word;
 
        /* We are expecting tcf_pedit to return a big endian value */
-       mask = (__force __be32)~tcf_pedit_mask(action, idx);
-       exact = (__force __be32)tcf_pedit_val(action, idx);
+       mask = (__force __be32)~act->mangle.mask;
+       exact = (__force __be32)act->mangle.val;
 
        if (exact & ~mask)
                return -EOPNOTSUPP;
@@ -541,7 +541,7 @@ nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off,
 }
 
 static int
-nfp_fl_set_tport(const struct tc_action *action, int idx, u32 off,
+nfp_fl_set_tport(const struct flow_action_entry *act, u32 off,
                 struct nfp_fl_set_tport *set_tport, int opcode)
 {
        u32 exact, mask;
@@ -549,8 +549,8 @@ nfp_fl_set_tport(const struct tc_action *action, int idx, u32 off,
        if (off)
                return -EOPNOTSUPP;
 
-       mask = ~tcf_pedit_mask(action, idx);
-       exact = tcf_pedit_val(action, idx);
+       mask = ~act->mangle.mask;
+       exact = act->mangle.val;
 
        if (exact & ~mask)
                return -EOPNOTSUPP;
@@ -584,20 +584,22 @@ static u32 nfp_fl_csum_l4_to_flag(u8 ip_proto)
 }
 
 static int
-nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
+nfp_fl_pedit(const struct flow_action_entry *act,
+            struct tc_cls_flower_offload *flow,
             char *nfp_action, int *a_len, u32 *csum_updated)
 {
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(flow);
        struct nfp_fl_set_ipv6_addr set_ip6_dst, set_ip6_src;
        struct nfp_fl_set_ipv6_tc_hl_fl set_ip6_tc_hl_fl;
        struct nfp_fl_set_ip4_ttl_tos set_ip_ttl_tos;
        struct nfp_fl_set_ip4_addrs set_ip_addr;
+       enum flow_action_mangle_base htype;
        struct nfp_fl_set_tport set_tport;
        struct nfp_fl_set_eth set_eth;
-       enum pedit_header_type htype;
-       int idx, nkeys, err;
        size_t act_size = 0;
-       u32 offset, cmd;
        u8 ip_proto = 0;
+       u32 offset;
+       int err;
 
        memset(&set_ip6_tc_hl_fl, 0, sizeof(set_ip6_tc_hl_fl));
        memset(&set_ip_ttl_tos, 0, sizeof(set_ip_ttl_tos));
@@ -606,50 +608,41 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
        memset(&set_ip_addr, 0, sizeof(set_ip_addr));
        memset(&set_tport, 0, sizeof(set_tport));
        memset(&set_eth, 0, sizeof(set_eth));
-       nkeys = tcf_pedit_nkeys(action);
-
-       for (idx = 0; idx < nkeys; idx++) {
-               cmd = tcf_pedit_cmd(action, idx);
-               htype = tcf_pedit_htype(action, idx);
-               offset = tcf_pedit_offset(action, idx);
 
-               if (cmd != TCA_PEDIT_KEY_EX_CMD_SET)
-                       return -EOPNOTSUPP;
+       htype = act->mangle.htype;
+       offset = act->mangle.offset;
 
-               switch (htype) {
-               case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
-                       err = nfp_fl_set_eth(action, idx, offset, &set_eth);
-                       break;
-               case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
-                       err = nfp_fl_set_ip4(action, idx, offset, &set_ip_addr,
-                                            &set_ip_ttl_tos);
-                       break;
-               case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
-                       err = nfp_fl_set_ip6(action, idx, offset, &set_ip6_dst,
-                                            &set_ip6_src, &set_ip6_tc_hl_fl);
-                       break;
-               case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
-                       err = nfp_fl_set_tport(action, idx, offset, &set_tport,
-                                              NFP_FL_ACTION_OPCODE_SET_TCP);
-                       break;
-               case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
-                       err = nfp_fl_set_tport(action, idx, offset, &set_tport,
-                                              NFP_FL_ACTION_OPCODE_SET_UDP);
-                       break;
-               default:
-                       return -EOPNOTSUPP;
-               }
-               if (err)
-                       return err;
+       switch (htype) {
+       case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
+               err = nfp_fl_set_eth(act, offset, &set_eth);
+               break;
+       case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
+               err = nfp_fl_set_ip4(act, offset, &set_ip_addr,
+                                    &set_ip_ttl_tos);
+               break;
+       case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
+               err = nfp_fl_set_ip6(act, offset, &set_ip6_dst,
+                                    &set_ip6_src, &set_ip6_tc_hl_fl);
+               break;
+       case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
+               err = nfp_fl_set_tport(act, offset, &set_tport,
+                                      NFP_FL_ACTION_OPCODE_SET_TCP);
+               break;
+       case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
+               err = nfp_fl_set_tport(act, offset, &set_tport,
+                                      NFP_FL_ACTION_OPCODE_SET_UDP);
+               break;
+       default:
+               return -EOPNOTSUPP;
        }
+       if (err)
+               return err;
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *basic;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
 
-               basic = skb_flow_dissector_target(flow->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 flow->key);
-               ip_proto = basic->ip_proto;
+               flow_rule_match_basic(rule, &match);
+               ip_proto = match.key->ip_proto;
        }
 
        if (set_eth.head.len_lw) {
@@ -733,7 +726,7 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
 }
 
 static int
-nfp_flower_output_action(struct nfp_app *app, const struct tc_action *a,
+nfp_flower_output_action(struct nfp_app *app, const struct flow_action_entry *act,
                         struct nfp_fl_payload *nfp_fl, int *a_len,
                         struct net_device *netdev, bool last,
                         enum nfp_flower_tun_type *tun_type, int *tun_out_cnt,
@@ -753,7 +746,7 @@ nfp_flower_output_action(struct nfp_app *app, const struct tc_action *a,
                return -EOPNOTSUPP;
 
        output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len];
-       err = nfp_fl_output(app, output, a, nfp_fl, last, netdev, *tun_type,
+       err = nfp_fl_output(app, output, act, nfp_fl, last, netdev, *tun_type,
                            tun_out_cnt);
        if (err)
                return err;
@@ -764,7 +757,7 @@ nfp_flower_output_action(struct nfp_app *app, const struct tc_action *a,
                /* nfp_fl_pre_lag returns -err or size of prelag action added.
                 * This will be 0 if it is not egressing to a lag dev.
                 */
-               prelag_size = nfp_fl_pre_lag(app, a, nfp_fl, *a_len);
+               prelag_size = nfp_fl_pre_lag(app, act, nfp_fl, *a_len);
                if (prelag_size < 0)
                        return prelag_size;
                else if (prelag_size > 0 && (!last || *out_cnt))
@@ -778,7 +771,7 @@ nfp_flower_output_action(struct nfp_app *app, const struct tc_action *a,
 }
 
 static int
-nfp_flower_loop_action(struct nfp_app *app, const struct tc_action *a,
+nfp_flower_loop_action(struct nfp_app *app, const struct flow_action_entry *act,
                       struct tc_cls_flower_offload *flow,
                       struct nfp_fl_payload *nfp_fl, int *a_len,
                       struct net_device *netdev,
@@ -791,23 +784,25 @@ nfp_flower_loop_action(struct nfp_app *app, const struct tc_action *a,
        struct nfp_fl_pop_vlan *pop_v;
        int err;
 
-       if (is_tcf_gact_shot(a)) {
+       switch (act->id) {
+       case FLOW_ACTION_DROP:
                nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_DROP);
-       } else if (is_tcf_mirred_egress_redirect(a)) {
-               err = nfp_flower_output_action(app, a, nfp_fl, a_len, netdev,
+               break;
+       case FLOW_ACTION_REDIRECT:
+               err = nfp_flower_output_action(app, act, nfp_fl, a_len, netdev,
                                               true, tun_type, tun_out_cnt,
                                               out_cnt, csum_updated);
                if (err)
                        return err;
-
-       } else if (is_tcf_mirred_egress_mirror(a)) {
-               err = nfp_flower_output_action(app, a, nfp_fl, a_len, netdev,
+               break;
+       case FLOW_ACTION_MIRRED:
+               err = nfp_flower_output_action(app, act, nfp_fl, a_len, netdev,
                                               false, tun_type, tun_out_cnt,
                                               out_cnt, csum_updated);
                if (err)
                        return err;
-
-       } else if (is_tcf_vlan(a) && tcf_vlan_action(a) == TCA_VLAN_ACT_POP) {
+               break;
+       case FLOW_ACTION_VLAN_POP:
                if (*a_len + sizeof(struct nfp_fl_pop_vlan) > NFP_FL_MAX_A_SIZ)
                        return -EOPNOTSUPP;
 
@@ -816,19 +811,21 @@ nfp_flower_loop_action(struct nfp_app *app, const struct tc_action *a,
 
                nfp_fl_pop_vlan(pop_v);
                *a_len += sizeof(struct nfp_fl_pop_vlan);
-       } else if (is_tcf_vlan(a) && tcf_vlan_action(a) == TCA_VLAN_ACT_PUSH) {
+               break;
+       case FLOW_ACTION_VLAN_PUSH:
                if (*a_len + sizeof(struct nfp_fl_push_vlan) > NFP_FL_MAX_A_SIZ)
                        return -EOPNOTSUPP;
 
                psh_v = (struct nfp_fl_push_vlan *)&nfp_fl->action_data[*a_len];
                nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
 
-               nfp_fl_push_vlan(psh_v, a);
+               nfp_fl_push_vlan(psh_v, act);
                *a_len += sizeof(struct nfp_fl_push_vlan);
-       } else if (is_tcf_tunnel_set(a)) {
-               struct ip_tunnel_info *ip_tun = tcf_tunnel_info(a);
+               break;
+       case FLOW_ACTION_TUNNEL_ENCAP: {
+               const struct ip_tunnel_info *ip_tun = act->tunnel;
 
-               *tun_type = nfp_fl_get_tun_from_act_l4_port(app, a);
+               *tun_type = nfp_fl_get_tun_from_act_l4_port(app, act);
                if (*tun_type == NFP_FL_TUNNEL_NONE)
                        return -EOPNOTSUPP;
 
@@ -847,32 +844,36 @@ nfp_flower_loop_action(struct nfp_app *app, const struct tc_action *a,
                nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
                *a_len += sizeof(struct nfp_fl_pre_tunnel);
 
-               err = nfp_fl_push_geneve_options(nfp_fl, a_len, a);
+               err = nfp_fl_push_geneve_options(nfp_fl, a_len, act);
                if (err)
                        return err;
 
                set_tun = (void *)&nfp_fl->action_data[*a_len];
-               err = nfp_fl_set_ipv4_udp_tun(app, set_tun, a, pre_tun,
+               err = nfp_fl_set_ipv4_udp_tun(app, set_tun, act, pre_tun,
                                              *tun_type, netdev);
                if (err)
                        return err;
                *a_len += sizeof(struct nfp_fl_set_ipv4_udp_tun);
-       } else if (is_tcf_tunnel_release(a)) {
+               }
+               break;
+       case FLOW_ACTION_TUNNEL_DECAP:
                /* Tunnel decap is handled by default so accept action. */
                return 0;
-       } else if (is_tcf_pedit(a)) {
-               if (nfp_fl_pedit(a, flow, &nfp_fl->action_data[*a_len],
+       case FLOW_ACTION_MANGLE:
+               if (nfp_fl_pedit(act, flow, &nfp_fl->action_data[*a_len],
                                 a_len, csum_updated))
                        return -EOPNOTSUPP;
-       } else if (is_tcf_csum(a)) {
+               break;
+       case FLOW_ACTION_CSUM:
                /* csum action requests recalc of something we have not fixed */
-               if (tcf_csum_update_flags(a) & ~*csum_updated)
+               if (act->csum_flags & ~*csum_updated)
                        return -EOPNOTSUPP;
                /* If we will correctly fix the csum we can remove it from the
                 * csum update list. Which will later be used to check support.
                 */
-               *csum_updated &= ~tcf_csum_update_flags(a);
-       } else {
+               *csum_updated &= ~act->csum_flags;
+               break;
+       default:
                /* Currently we do not handle any other actions. */
                return -EOPNOTSUPP;
        }
@@ -887,7 +888,7 @@ int nfp_flower_compile_action(struct nfp_app *app,
 {
        int act_len, act_cnt, err, tun_out_cnt, out_cnt, i;
        enum nfp_flower_tun_type tun_type;
-       const struct tc_action *a;
+       struct flow_action_entry *act;
        u32 csum_updated = 0;
 
        memset(nfp_flow->action_data, 0, NFP_FL_MAX_A_SIZ);
@@ -898,8 +899,8 @@ int nfp_flower_compile_action(struct nfp_app *app,
        tun_out_cnt = 0;
        out_cnt = 0;
 
-       tcf_exts_for_each_action(i, a, flow->exts) {
-               err = nfp_flower_loop_action(app, a, flow, nfp_flow, &act_len,
+       flow_action_for_each(i, act, &flow->rule->action) {
+               err = nfp_flower_loop_action(app, act, flow, nfp_flow, &act_len,
                                             netdev, &tun_type, &tun_out_cnt,
                                             &out_cnt, &csum_updated);
                if (err)
index 56b22ea..cf9e111 100644 (file)
@@ -45,11 +45,9 @@ nfp_flower_cmsg_mac_repr_start(struct nfp_app *app, unsigned int num_ports)
 {
        struct nfp_flower_cmsg_mac_repr *msg;
        struct sk_buff *skb;
-       unsigned int size;
 
-       size = sizeof(*msg) + num_ports * sizeof(msg->ports[0]);
-       skb = nfp_flower_cmsg_alloc(app, size, NFP_FLOWER_CMSG_TYPE_MAC_REPR,
-                                   GFP_KERNEL);
+       skb = nfp_flower_cmsg_alloc(app, struct_size(msg, ports, num_ports),
+                                   NFP_FLOWER_CMSG_TYPE_MAC_REPR, GFP_KERNEL);
        if (!skb)
                return NULL;
 
index c04a0d6..e03c8ef 100644 (file)
@@ -8,31 +8,41 @@
 #include "main.h"
 
 static void
-nfp_flower_compile_meta_tci(struct nfp_flower_meta_tci *frame,
-                           struct tc_cls_flower_offload *flow, u8 key_type,
-                           bool mask_version)
+nfp_flower_compile_meta_tci(struct nfp_flower_meta_tci *ext,
+                           struct nfp_flower_meta_tci *msk,
+                           struct tc_cls_flower_offload *flow, u8 key_type)
 {
-       struct fl_flow_key *target = mask_version ? flow->mask : flow->key;
-       struct flow_dissector_key_vlan *flow_vlan;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(flow);
        u16 tmp_tci;
 
-       memset(frame, 0, sizeof(struct nfp_flower_meta_tci));
+       memset(ext, 0, sizeof(struct nfp_flower_meta_tci));
+       memset(msk, 0, sizeof(struct nfp_flower_meta_tci));
+
        /* Populate the metadata frame. */
-       frame->nfp_flow_key_layer = key_type;
-       frame->mask_id = ~0;
+       ext->nfp_flow_key_layer = key_type;
+       ext->mask_id = ~0;
+
+       msk->nfp_flow_key_layer = key_type;
+       msk->mask_id = ~0;
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
-               flow_vlan = skb_flow_dissector_target(flow->dissector,
-                                                     FLOW_DISSECTOR_KEY_VLAN,
-                                                     target);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+               struct flow_match_vlan match;
+
+               flow_rule_match_vlan(rule, &match);
                /* Populate the tci field. */
-               if (flow_vlan->vlan_id || flow_vlan->vlan_priority) {
+               if (match.key->vlan_id || match.key->vlan_priority) {
                        tmp_tci = FIELD_PREP(NFP_FLOWER_MASK_VLAN_PRIO,
-                                            flow_vlan->vlan_priority) |
+                                            match.key->vlan_priority) |
                                  FIELD_PREP(NFP_FLOWER_MASK_VLAN_VID,
-                                            flow_vlan->vlan_id) |
+                                            match.key->vlan_id) |
                                  NFP_FLOWER_MASK_VLAN_CFI;
-                       frame->tci = cpu_to_be16(tmp_tci);
+                       ext->tci = cpu_to_be16(tmp_tci);
+                       tmp_tci = FIELD_PREP(NFP_FLOWER_MASK_VLAN_PRIO,
+                                            match.mask->vlan_priority) |
+                                 FIELD_PREP(NFP_FLOWER_MASK_VLAN_VID,
+                                            match.mask->vlan_id) |
+                                 NFP_FLOWER_MASK_VLAN_CFI;
+                       msk->tci = cpu_to_be16(tmp_tci);
                }
        }
 }
@@ -64,231 +74,249 @@ nfp_flower_compile_port(struct nfp_flower_in_port *frame, u32 cmsg_port,
 }
 
 static void
-nfp_flower_compile_mac(struct nfp_flower_mac_mpls *frame,
-                      struct tc_cls_flower_offload *flow,
-                      bool mask_version)
+nfp_flower_compile_mac(struct nfp_flower_mac_mpls *ext,
+                      struct nfp_flower_mac_mpls *msk,
+                      struct tc_cls_flower_offload *flow)
 {
-       struct fl_flow_key *target = mask_version ? flow->mask : flow->key;
-       struct flow_dissector_key_eth_addrs *addr;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(flow);
+
+       memset(ext, 0, sizeof(struct nfp_flower_mac_mpls));
+       memset(msk, 0, sizeof(struct nfp_flower_mac_mpls));
 
-       memset(frame, 0, sizeof(struct nfp_flower_mac_mpls));
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct flow_match_eth_addrs match;
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
-               addr = skb_flow_dissector_target(flow->dissector,
-                                                FLOW_DISSECTOR_KEY_ETH_ADDRS,
-                                                target);
+               flow_rule_match_eth_addrs(rule, &match);
                /* Populate mac frame. */
-               ether_addr_copy(frame->mac_dst, &addr->dst[0]);
-               ether_addr_copy(frame->mac_src, &addr->src[0]);
+               ether_addr_copy(ext->mac_dst, &match.key->dst[0]);
+               ether_addr_copy(ext->mac_src, &match.key->src[0]);
+               ether_addr_copy(msk->mac_dst, &match.mask->dst[0]);
+               ether_addr_copy(msk->mac_src, &match.mask->src[0]);
        }
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_MPLS)) {
-               struct flow_dissector_key_mpls *mpls;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_MPLS)) {
+               struct flow_match_mpls match;
                u32 t_mpls;
 
-               mpls = skb_flow_dissector_target(flow->dissector,
-                                                FLOW_DISSECTOR_KEY_MPLS,
-                                                target);
-
-               t_mpls = FIELD_PREP(NFP_FLOWER_MASK_MPLS_LB, mpls->mpls_label) |
-                        FIELD_PREP(NFP_FLOWER_MASK_MPLS_TC, mpls->mpls_tc) |
-                        FIELD_PREP(NFP_FLOWER_MASK_MPLS_BOS, mpls->mpls_bos) |
+               flow_rule_match_mpls(rule, &match);
+               t_mpls = FIELD_PREP(NFP_FLOWER_MASK_MPLS_LB, match.key->mpls_label) |
+                        FIELD_PREP(NFP_FLOWER_MASK_MPLS_TC, match.key->mpls_tc) |
+                        FIELD_PREP(NFP_FLOWER_MASK_MPLS_BOS, match.key->mpls_bos) |
                         NFP_FLOWER_MASK_MPLS_Q;
-
-               frame->mpls_lse = cpu_to_be32(t_mpls);
-       } else if (dissector_uses_key(flow->dissector,
-                                     FLOW_DISSECTOR_KEY_BASIC)) {
+               ext->mpls_lse = cpu_to_be32(t_mpls);
+               t_mpls = FIELD_PREP(NFP_FLOWER_MASK_MPLS_LB, match.mask->mpls_label) |
+                        FIELD_PREP(NFP_FLOWER_MASK_MPLS_TC, match.mask->mpls_tc) |
+                        FIELD_PREP(NFP_FLOWER_MASK_MPLS_BOS, match.mask->mpls_bos) |
+                        NFP_FLOWER_MASK_MPLS_Q;
+               msk->mpls_lse = cpu_to_be32(t_mpls);
+       } else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
                /* Check for mpls ether type and set NFP_FLOWER_MASK_MPLS_Q
                 * bit, which indicates an mpls ether type but without any
                 * mpls fields.
                 */
-               struct flow_dissector_key_basic *key_basic;
-
-               key_basic = skb_flow_dissector_target(flow->dissector,
-                                                     FLOW_DISSECTOR_KEY_BASIC,
-                                                     flow->key);
-               if (key_basic->n_proto == cpu_to_be16(ETH_P_MPLS_UC) ||
-                   key_basic->n_proto == cpu_to_be16(ETH_P_MPLS_MC))
-                       frame->mpls_lse = cpu_to_be32(NFP_FLOWER_MASK_MPLS_Q);
+               struct flow_match_basic match;
+
+               flow_rule_match_basic(rule, &match);
+               if (match.key->n_proto == cpu_to_be16(ETH_P_MPLS_UC) ||
+                   match.key->n_proto == cpu_to_be16(ETH_P_MPLS_MC)) {
+                       ext->mpls_lse = cpu_to_be32(NFP_FLOWER_MASK_MPLS_Q);
+                       msk->mpls_lse = cpu_to_be32(NFP_FLOWER_MASK_MPLS_Q);
+               }
        }
 }
 
 static void
-nfp_flower_compile_tport(struct nfp_flower_tp_ports *frame,
-                        struct tc_cls_flower_offload *flow,
-                        bool mask_version)
+nfp_flower_compile_tport(struct nfp_flower_tp_ports *ext,
+                        struct nfp_flower_tp_ports *msk,
+                        struct tc_cls_flower_offload *flow)
 {
-       struct fl_flow_key *target = mask_version ? flow->mask : flow->key;
-       struct flow_dissector_key_ports *tp;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(flow);
+
+       memset(ext, 0, sizeof(struct nfp_flower_tp_ports));
+       memset(msk, 0, sizeof(struct nfp_flower_tp_ports));
 
-       memset(frame, 0, sizeof(struct nfp_flower_tp_ports));
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+               struct flow_match_ports match;
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
-               tp = skb_flow_dissector_target(flow->dissector,
-                                              FLOW_DISSECTOR_KEY_PORTS,
-                                              target);
-               frame->port_src = tp->src;
-               frame->port_dst = tp->dst;
+               flow_rule_match_ports(rule, &match);
+               ext->port_src = match.key->src;
+               ext->port_dst = match.key->dst;
+               msk->port_src = match.mask->src;
+               msk->port_dst = match.mask->dst;
        }
 }
 
 static void
-nfp_flower_compile_ip_ext(struct nfp_flower_ip_ext *frame,
-                         struct tc_cls_flower_offload *flow,
-                         bool mask_version)
+nfp_flower_compile_ip_ext(struct nfp_flower_ip_ext *ext,
+                         struct nfp_flower_ip_ext *msk,
+                         struct tc_cls_flower_offload *flow)
 {
-       struct fl_flow_key *target = mask_version ? flow->mask : flow->key;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(flow);
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *basic;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
 
-               basic = skb_flow_dissector_target(flow->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 target);
-               frame->proto = basic->ip_proto;
+               flow_rule_match_basic(rule, &match);
+               ext->proto = match.key->ip_proto;
+               msk->proto = match.mask->ip_proto;
        }
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_IP)) {
-               struct flow_dissector_key_ip *flow_ip;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
+               struct flow_match_ip match;
 
-               flow_ip = skb_flow_dissector_target(flow->dissector,
-                                                   FLOW_DISSECTOR_KEY_IP,
-                                                   target);
-               frame->tos = flow_ip->tos;
-               frame->ttl = flow_ip->ttl;
+               flow_rule_match_ip(rule, &match);
+               ext->tos = match.key->tos;
+               ext->ttl = match.key->ttl;
+               msk->tos = match.mask->tos;
+               msk->ttl = match.mask->ttl;
        }
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_TCP)) {
-               struct flow_dissector_key_tcp *tcp;
-               u32 tcp_flags;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) {
+               u16 tcp_flags, tcp_flags_mask;
+               struct flow_match_tcp match;
 
-               tcp = skb_flow_dissector_target(flow->dissector,
-                                               FLOW_DISSECTOR_KEY_TCP, target);
-               tcp_flags = be16_to_cpu(tcp->flags);
+               flow_rule_match_tcp(rule, &match);
+               tcp_flags = be16_to_cpu(match.key->flags);
+               tcp_flags_mask = be16_to_cpu(match.mask->flags);
 
                if (tcp_flags & TCPHDR_FIN)
-                       frame->flags |= NFP_FL_TCP_FLAG_FIN;
+                       ext->flags |= NFP_FL_TCP_FLAG_FIN;
+               if (tcp_flags_mask & TCPHDR_FIN)
+                       msk->flags |= NFP_FL_TCP_FLAG_FIN;
+
                if (tcp_flags & TCPHDR_SYN)
-                       frame->flags |= NFP_FL_TCP_FLAG_SYN;
+                       ext->flags |= NFP_FL_TCP_FLAG_SYN;
+               if (tcp_flags_mask & TCPHDR_SYN)
+                       msk->flags |= NFP_FL_TCP_FLAG_SYN;
+
                if (tcp_flags & TCPHDR_RST)
-                       frame->flags |= NFP_FL_TCP_FLAG_RST;
+                       ext->flags |= NFP_FL_TCP_FLAG_RST;
+               if (tcp_flags_mask & TCPHDR_RST)
+                       msk->flags |= NFP_FL_TCP_FLAG_RST;
+
                if (tcp_flags & TCPHDR_PSH)
-                       frame->flags |= NFP_FL_TCP_FLAG_PSH;
+                       ext->flags |= NFP_FL_TCP_FLAG_PSH;
+               if (tcp_flags_mask & TCPHDR_PSH)
+                       msk->flags |= NFP_FL_TCP_FLAG_PSH;
+
                if (tcp_flags & TCPHDR_URG)
-                       frame->flags |= NFP_FL_TCP_FLAG_URG;
+                       ext->flags |= NFP_FL_TCP_FLAG_URG;
+               if (tcp_flags_mask & TCPHDR_URG)
+                       msk->flags |= NFP_FL_TCP_FLAG_URG;
        }
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
-               struct flow_dissector_key_control *key;
-
-               key = skb_flow_dissector_target(flow->dissector,
-                                               FLOW_DISSECTOR_KEY_CONTROL,
-                                               target);
-               if (key->flags & FLOW_DIS_IS_FRAGMENT)
-                       frame->flags |= NFP_FL_IP_FRAGMENTED;
-               if (key->flags & FLOW_DIS_FIRST_FRAG)
-                       frame->flags |= NFP_FL_IP_FRAG_FIRST;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+               struct flow_match_control match;
+
+               flow_rule_match_control(rule, &match);
+               if (match.key->flags & FLOW_DIS_IS_FRAGMENT)
+                       ext->flags |= NFP_FL_IP_FRAGMENTED;
+               if (match.mask->flags & FLOW_DIS_IS_FRAGMENT)
+                       msk->flags |= NFP_FL_IP_FRAGMENTED;
+               if (match.key->flags & FLOW_DIS_FIRST_FRAG)
+                       ext->flags |= NFP_FL_IP_FRAG_FIRST;
+               if (match.mask->flags & FLOW_DIS_FIRST_FRAG)
+                       msk->flags |= NFP_FL_IP_FRAG_FIRST;
        }
 }
 
 static void
-nfp_flower_compile_ipv4(struct nfp_flower_ipv4 *frame,
-                       struct tc_cls_flower_offload *flow,
-                       bool mask_version)
+nfp_flower_compile_ipv4(struct nfp_flower_ipv4 *ext,
+                       struct nfp_flower_ipv4 *msk,
+                       struct tc_cls_flower_offload *flow)
 {
-       struct fl_flow_key *target = mask_version ? flow->mask : flow->key;
-       struct flow_dissector_key_ipv4_addrs *addr;
-
-       memset(frame, 0, sizeof(struct nfp_flower_ipv4));
-
-       if (dissector_uses_key(flow->dissector,
-                              FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
-               addr = skb_flow_dissector_target(flow->dissector,
-                                                FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                                target);
-               frame->ipv4_src = addr->src;
-               frame->ipv4_dst = addr->dst;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(flow);
+       struct flow_match_ipv4_addrs match;
+
+       memset(ext, 0, sizeof(struct nfp_flower_ipv4));
+       memset(msk, 0, sizeof(struct nfp_flower_ipv4));
+
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
+               flow_rule_match_ipv4_addrs(rule, &match);
+               ext->ipv4_src = match.key->src;
+               ext->ipv4_dst = match.key->dst;
+               msk->ipv4_src = match.mask->src;
+               msk->ipv4_dst = match.mask->dst;
        }
 
-       nfp_flower_compile_ip_ext(&frame->ip_ext, flow, mask_version);
+       nfp_flower_compile_ip_ext(&ext->ip_ext, &msk->ip_ext, flow);
 }
 
 static void
-nfp_flower_compile_ipv6(struct nfp_flower_ipv6 *frame,
-                       struct tc_cls_flower_offload *flow,
-                       bool mask_version)
+nfp_flower_compile_ipv6(struct nfp_flower_ipv6 *ext,
+                       struct nfp_flower_ipv6 *msk,
+                       struct tc_cls_flower_offload *flow)
 {
-       struct fl_flow_key *target = mask_version ? flow->mask : flow->key;
-       struct flow_dissector_key_ipv6_addrs *addr;
-
-       memset(frame, 0, sizeof(struct nfp_flower_ipv6));
-
-       if (dissector_uses_key(flow->dissector,
-                              FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
-               addr = skb_flow_dissector_target(flow->dissector,
-                                                FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                                target);
-               frame->ipv6_src = addr->src;
-               frame->ipv6_dst = addr->dst;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(flow);
+
+       memset(ext, 0, sizeof(struct nfp_flower_ipv6));
+       memset(msk, 0, sizeof(struct nfp_flower_ipv6));
+
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
+               struct flow_match_ipv6_addrs match;
+
+               flow_rule_match_ipv6_addrs(rule, &match);
+               ext->ipv6_src = match.key->src;
+               ext->ipv6_dst = match.key->dst;
+               msk->ipv6_src = match.mask->src;
+               msk->ipv6_dst = match.mask->dst;
        }
 
-       nfp_flower_compile_ip_ext(&frame->ip_ext, flow, mask_version);
+       nfp_flower_compile_ip_ext(&ext->ip_ext, &msk->ip_ext, flow);
 }
 
 static int
-nfp_flower_compile_geneve_opt(void *key_buf, struct tc_cls_flower_offload *flow,
-                             bool mask_version)
+nfp_flower_compile_geneve_opt(void *ext, void *msk,
+                             struct tc_cls_flower_offload *flow)
 {
-       struct fl_flow_key *target = mask_version ? flow->mask : flow->key;
-       struct flow_dissector_key_enc_opts *opts;
+       struct flow_match_enc_opts match;
 
-       opts = skb_flow_dissector_target(flow->dissector,
-                                        FLOW_DISSECTOR_KEY_ENC_OPTS,
-                                        target);
-       memcpy(key_buf, opts->data, opts->len);
+       flow_rule_match_enc_opts(flow->rule, &match);
+       memcpy(ext, match.key->data, match.key->len);
+       memcpy(msk, match.mask->data, match.mask->len);
 
        return 0;
 }
 
 static void
-nfp_flower_compile_ipv4_udp_tun(struct nfp_flower_ipv4_udp_tun *frame,
-                               struct tc_cls_flower_offload *flow,
-                               bool mask_version)
+nfp_flower_compile_ipv4_udp_tun(struct nfp_flower_ipv4_udp_tun *ext,
+                               struct nfp_flower_ipv4_udp_tun *msk,
+                               struct tc_cls_flower_offload *flow)
 {
-       struct fl_flow_key *target = mask_version ? flow->mask : flow->key;
-       struct flow_dissector_key_ipv4_addrs *tun_ips;
-       struct flow_dissector_key_keyid *vni;
-       struct flow_dissector_key_ip *ip;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(flow);
 
-       memset(frame, 0, sizeof(struct nfp_flower_ipv4_udp_tun));
+       memset(ext, 0, sizeof(struct nfp_flower_ipv4_udp_tun));
+       memset(msk, 0, sizeof(struct nfp_flower_ipv4_udp_tun));
 
-       if (dissector_uses_key(flow->dissector,
-                              FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+               struct flow_match_enc_keyid match;
                u32 temp_vni;
 
-               vni = skb_flow_dissector_target(flow->dissector,
-                                               FLOW_DISSECTOR_KEY_ENC_KEYID,
-                                               target);
-               temp_vni = be32_to_cpu(vni->keyid) << NFP_FL_TUN_VNI_OFFSET;
-               frame->tun_id = cpu_to_be32(temp_vni);
+               flow_rule_match_enc_keyid(rule, &match);
+               temp_vni = be32_to_cpu(match.key->keyid) << NFP_FL_TUN_VNI_OFFSET;
+               ext->tun_id = cpu_to_be32(temp_vni);
+               temp_vni = be32_to_cpu(match.mask->keyid) << NFP_FL_TUN_VNI_OFFSET;
+               msk->tun_id = cpu_to_be32(temp_vni);
        }
 
-       if (dissector_uses_key(flow->dissector,
-                              FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) {
-               tun_ips =
-                  skb_flow_dissector_target(flow->dissector,
-                                            FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS,
-                                            target);
-               frame->ip_src = tun_ips->src;
-               frame->ip_dst = tun_ips->dst;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) {
+               struct flow_match_ipv4_addrs match;
+
+               flow_rule_match_enc_ipv4_addrs(rule, &match);
+               ext->ip_src = match.key->src;
+               ext->ip_dst = match.key->dst;
+               msk->ip_src = match.mask->src;
+               msk->ip_dst = match.mask->dst;
        }
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_ENC_IP)) {
-               ip = skb_flow_dissector_target(flow->dissector,
-                                              FLOW_DISSECTOR_KEY_ENC_IP,
-                                              target);
-               frame->tos = ip->tos;
-               frame->ttl = ip->ttl;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IP)) {
+               struct flow_match_ip match;
+
+               flow_rule_match_enc_ip(rule, &match);
+               ext->tos = match.key->tos;
+               ext->ttl = match.key->ttl;
+               msk->tos = match.mask->tos;
+               msk->ttl = match.mask->ttl;
        }
 }
 
@@ -313,12 +341,9 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
        ext = nfp_flow->unmasked_data;
        msk = nfp_flow->mask_data;
 
-       /* Populate Exact Metadata. */
        nfp_flower_compile_meta_tci((struct nfp_flower_meta_tci *)ext,
-                                   flow, key_ls->key_layer, false);
-       /* Populate Mask Metadata. */
-       nfp_flower_compile_meta_tci((struct nfp_flower_meta_tci *)msk,
-                                   flow, key_ls->key_layer, true);
+                                   (struct nfp_flower_meta_tci *)msk,
+                                   flow, key_ls->key_layer);
        ext += sizeof(struct nfp_flower_meta_tci);
        msk += sizeof(struct nfp_flower_meta_tci);
 
@@ -348,45 +373,33 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
        msk += sizeof(struct nfp_flower_in_port);
 
        if (NFP_FLOWER_LAYER_MAC & key_ls->key_layer) {
-               /* Populate Exact MAC Data. */
                nfp_flower_compile_mac((struct nfp_flower_mac_mpls *)ext,
-                                      flow, false);
-               /* Populate Mask MAC Data. */
-               nfp_flower_compile_mac((struct nfp_flower_mac_mpls *)msk,
-                                      flow, true);
+                                      (struct nfp_flower_mac_mpls *)msk,
+                                      flow);
                ext += sizeof(struct nfp_flower_mac_mpls);
                msk += sizeof(struct nfp_flower_mac_mpls);
        }
 
        if (NFP_FLOWER_LAYER_TP & key_ls->key_layer) {
-               /* Populate Exact TP Data. */
                nfp_flower_compile_tport((struct nfp_flower_tp_ports *)ext,
-                                        flow, false);
-               /* Populate Mask TP Data. */
-               nfp_flower_compile_tport((struct nfp_flower_tp_ports *)msk,
-                                        flow, true);
+                                        (struct nfp_flower_tp_ports *)msk,
+                                        flow);
                ext += sizeof(struct nfp_flower_tp_ports);
                msk += sizeof(struct nfp_flower_tp_ports);
        }
 
        if (NFP_FLOWER_LAYER_IPV4 & key_ls->key_layer) {
-               /* Populate Exact IPv4 Data. */
                nfp_flower_compile_ipv4((struct nfp_flower_ipv4 *)ext,
-                                       flow, false);
-               /* Populate Mask IPv4 Data. */
-               nfp_flower_compile_ipv4((struct nfp_flower_ipv4 *)msk,
-                                       flow, true);
+                                       (struct nfp_flower_ipv4 *)msk,
+                                       flow);
                ext += sizeof(struct nfp_flower_ipv4);
                msk += sizeof(struct nfp_flower_ipv4);
        }
 
        if (NFP_FLOWER_LAYER_IPV6 & key_ls->key_layer) {
-               /* Populate Exact IPv4 Data. */
                nfp_flower_compile_ipv6((struct nfp_flower_ipv6 *)ext,
-                                       flow, false);
-               /* Populate Mask IPv4 Data. */
-               nfp_flower_compile_ipv6((struct nfp_flower_ipv6 *)msk,
-                                       flow, true);
+                                       (struct nfp_flower_ipv6 *)msk,
+                                       flow);
                ext += sizeof(struct nfp_flower_ipv6);
                msk += sizeof(struct nfp_flower_ipv6);
        }
@@ -395,10 +408,7 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
            key_ls->key_layer_two & NFP_FLOWER_LAYER2_GENEVE) {
                __be32 tun_dst;
 
-               /* Populate Exact VXLAN Data. */
-               nfp_flower_compile_ipv4_udp_tun((void *)ext, flow, false);
-               /* Populate Mask VXLAN Data. */
-               nfp_flower_compile_ipv4_udp_tun((void *)msk, flow, true);
+               nfp_flower_compile_ipv4_udp_tun((void *)ext, (void *)msk, flow);
                tun_dst = ((struct nfp_flower_ipv4_udp_tun *)ext)->ip_dst;
                ext += sizeof(struct nfp_flower_ipv4_udp_tun);
                msk += sizeof(struct nfp_flower_ipv4_udp_tun);
@@ -410,11 +420,7 @@ int nfp_flower_compile_flow_match(struct nfp_app *app,
                nfp_tunnel_add_ipv4_off(app, tun_dst);
 
                if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_GENEVE_OP) {
-                       err = nfp_flower_compile_geneve_opt(ext, flow, false);
-                       if (err)
-                               return err;
-
-                       err = nfp_flower_compile_geneve_opt(msk, flow, true);
+                       err = nfp_flower_compile_geneve_opt(ext, msk, flow);
                        if (err)
                                return err;
                }
index 2cdbf29..450d729 100644 (file)
@@ -102,23 +102,22 @@ nfp_flower_xmit_flow(struct nfp_app *app, struct nfp_fl_payload *nfp_flow,
 
 static bool nfp_flower_check_higher_than_mac(struct tc_cls_flower_offload *f)
 {
-       return dissector_uses_key(f->dissector,
-                                 FLOW_DISSECTOR_KEY_IPV4_ADDRS) ||
-               dissector_uses_key(f->dissector,
-                                  FLOW_DISSECTOR_KEY_IPV6_ADDRS) ||
-               dissector_uses_key(f->dissector,
-                                  FLOW_DISSECTOR_KEY_PORTS) ||
-               dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ICMP);
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
+
+       return flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) ||
+              flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) ||
+              flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS) ||
+              flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ICMP);
 }
 
 static int
-nfp_flower_calc_opt_layer(struct flow_dissector_key_enc_opts *enc_opts,
+nfp_flower_calc_opt_layer(struct flow_match_enc_opts *enc_opts,
                          u32 *key_layer_two, int *key_size)
 {
-       if (enc_opts->len > NFP_FL_MAX_GENEVE_OPT_KEY)
+       if (enc_opts->key->len > NFP_FL_MAX_GENEVE_OPT_KEY)
                return -EOPNOTSUPP;
 
-       if (enc_opts->len > 0) {
+       if (enc_opts->key->len > 0) {
                *key_layer_two |= NFP_FLOWER_LAYER2_GENEVE_OP;
                *key_size += sizeof(struct nfp_flower_geneve_options);
        }
@@ -133,20 +132,21 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
                                struct tc_cls_flower_offload *flow,
                                enum nfp_flower_tun_type *tun_type)
 {
-       struct flow_dissector_key_basic *mask_basic = NULL;
-       struct flow_dissector_key_basic *key_basic = NULL;
+       struct flow_rule *rule = tc_cls_flower_offload_flow_rule(flow);
+       struct flow_dissector *dissector = rule->match.dissector;
+       struct flow_match_basic basic = { NULL, NULL};
        struct nfp_flower_priv *priv = app->priv;
        u32 key_layer_two;
        u8 key_layer;
        int key_size;
        int err;
 
-       if (flow->dissector->used_keys & ~NFP_FLOWER_WHITELIST_DISSECTOR)
+       if (dissector->used_keys & ~NFP_FLOWER_WHITELIST_DISSECTOR)
                return -EOPNOTSUPP;
 
        /* If any tun dissector is used then the required set must be used. */
-       if (flow->dissector->used_keys & NFP_FLOWER_WHITELIST_TUN_DISSECTOR &&
-           (flow->dissector->used_keys & NFP_FLOWER_WHITELIST_TUN_DISSECTOR_R)
+       if (dissector->used_keys & NFP_FLOWER_WHITELIST_TUN_DISSECTOR &&
+           (dissector->used_keys & NFP_FLOWER_WHITELIST_TUN_DISSECTOR_R)
            != NFP_FLOWER_WHITELIST_TUN_DISSECTOR_R)
                return -EOPNOTSUPP;
 
@@ -155,76 +155,52 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
        key_size = sizeof(struct nfp_flower_meta_tci) +
                   sizeof(struct nfp_flower_in_port);
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS) ||
-           dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_MPLS)) {
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS) ||
+           flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_MPLS)) {
                key_layer |= NFP_FLOWER_LAYER_MAC;
                key_size += sizeof(struct nfp_flower_mac_mpls);
        }
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
-               struct flow_dissector_key_vlan *flow_vlan;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+               struct flow_match_vlan vlan;
 
-               flow_vlan = skb_flow_dissector_target(flow->dissector,
-                                                     FLOW_DISSECTOR_KEY_VLAN,
-                                                     flow->mask);
+               flow_rule_match_vlan(rule, &vlan);
                if (!(priv->flower_ext_feats & NFP_FL_FEATS_VLAN_PCP) &&
-                   flow_vlan->vlan_priority)
+                   vlan.key->vlan_priority)
                        return -EOPNOTSUPP;
        }
 
-       if (dissector_uses_key(flow->dissector,
-                              FLOW_DISSECTOR_KEY_ENC_CONTROL)) {
-               struct flow_dissector_key_ipv4_addrs *mask_ipv4 = NULL;
-               struct flow_dissector_key_ports *mask_enc_ports = NULL;
-               struct flow_dissector_key_enc_opts *enc_op = NULL;
-               struct flow_dissector_key_ports *enc_ports = NULL;
-               struct flow_dissector_key_control *mask_enc_ctl =
-                       skb_flow_dissector_target(flow->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_CONTROL,
-                                                 flow->mask);
-               struct flow_dissector_key_control *enc_ctl =
-                       skb_flow_dissector_target(flow->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_CONTROL,
-                                                 flow->key);
-
-               if (mask_enc_ctl->addr_type != 0xffff ||
-                   enc_ctl->addr_type != FLOW_DISSECTOR_KEY_IPV4_ADDRS)
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL)) {
+               struct flow_match_enc_opts enc_op = { NULL, NULL };
+               struct flow_match_ipv4_addrs ipv4_addrs;
+               struct flow_match_control enc_ctl;
+               struct flow_match_ports enc_ports;
+
+               flow_rule_match_enc_control(rule, &enc_ctl);
+
+               if (enc_ctl.mask->addr_type != 0xffff ||
+                   enc_ctl.key->addr_type != FLOW_DISSECTOR_KEY_IPV4_ADDRS)
                        return -EOPNOTSUPP;
 
                /* These fields are already verified as used. */
-               mask_ipv4 =
-                       skb_flow_dissector_target(flow->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS,
-                                                 flow->mask);
-               if (mask_ipv4->dst != cpu_to_be32(~0))
+               flow_rule_match_enc_ipv4_addrs(rule, &ipv4_addrs);
+               if (ipv4_addrs.mask->dst != cpu_to_be32(~0))
                        return -EOPNOTSUPP;
 
-               mask_enc_ports =
-                       skb_flow_dissector_target(flow->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_PORTS,
-                                                 flow->mask);
-               enc_ports =
-                       skb_flow_dissector_target(flow->dissector,
-                                                 FLOW_DISSECTOR_KEY_ENC_PORTS,
-                                                 flow->key);
-
-               if (mask_enc_ports->dst != cpu_to_be16(~0))
+               flow_rule_match_enc_ports(rule, &enc_ports);
+               if (enc_ports.mask->dst != cpu_to_be16(~0))
                        return -EOPNOTSUPP;
 
-               if (dissector_uses_key(flow->dissector,
-                                      FLOW_DISSECTOR_KEY_ENC_OPTS)) {
-                       enc_op = skb_flow_dissector_target(flow->dissector,
-                                                          FLOW_DISSECTOR_KEY_ENC_OPTS,
-                                                          flow->key);
-               }
+               if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_OPTS))
+                       flow_rule_match_enc_opts(rule, &enc_op);
 
-               switch (enc_ports->dst) {
+               switch (enc_ports.key->dst) {
                case htons(NFP_FL_VXLAN_PORT):
                        *tun_type = NFP_FL_TUNNEL_VXLAN;
                        key_layer |= NFP_FLOWER_LAYER_VXLAN;
                        key_size += sizeof(struct nfp_flower_ipv4_udp_tun);
 
-                       if (enc_op)
+                       if (enc_op.key)
                                return -EOPNOTSUPP;
                        break;
                case htons(NFP_FL_GENEVE_PORT):
@@ -236,11 +212,11 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
                        key_layer_two |= NFP_FLOWER_LAYER2_GENEVE;
                        key_size += sizeof(struct nfp_flower_ipv4_udp_tun);
 
-                       if (!enc_op)
+                       if (!enc_op.key)
                                break;
                        if (!(priv->flower_ext_feats & NFP_FL_FEATS_GENEVE_OPT))
                                return -EOPNOTSUPP;
-                       err = nfp_flower_calc_opt_layer(enc_op, &key_layer_two,
+                       err = nfp_flower_calc_opt_layer(&enc_op, &key_layer_two,
                                                        &key_size);
                        if (err)
                                return err;
@@ -254,19 +230,12 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
                        return -EOPNOTSUPP;
        }
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               mask_basic = skb_flow_dissector_target(flow->dissector,
-                                                      FLOW_DISSECTOR_KEY_BASIC,
-                                                      flow->mask);
-
-               key_basic = skb_flow_dissector_target(flow->dissector,
-                                                     FLOW_DISSECTOR_KEY_BASIC,
-                                                     flow->key);
-       }
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC))
+               flow_rule_match_basic(rule, &basic);
 
-       if (mask_basic && mask_basic->n_proto) {
+       if (basic.mask && basic.mask->n_proto) {
                /* Ethernet type is present in the key. */
-               switch (key_basic->n_proto) {
+               switch (basic.key->n_proto) {
                case cpu_to_be16(ETH_P_IP):
                        key_layer |= NFP_FLOWER_LAYER_IPV4;
                        key_size += sizeof(struct nfp_flower_ipv4);
@@ -305,9 +274,9 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
                }
        }
 
-       if (mask_basic && mask_basic->ip_proto) {
+       if (basic.mask && basic.mask->ip_proto) {
                /* Ethernet type is present in the key. */
-               switch (key_basic->ip_proto) {
+               switch (basic.key->ip_proto) {
                case IPPROTO_TCP:
                case IPPROTO_UDP:
                case IPPROTO_SCTP:
@@ -324,14 +293,12 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
                }
        }
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_TCP)) {
-               struct flow_dissector_key_tcp *tcp;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) {
+               struct flow_match_tcp tcp;
                u32 tcp_flags;
 
-               tcp = skb_flow_dissector_target(flow->dissector,
-                                               FLOW_DISSECTOR_KEY_TCP,
-                                               flow->key);
-               tcp_flags = be16_to_cpu(tcp->flags);
+               flow_rule_match_tcp(rule, &tcp);
+               tcp_flags = be16_to_cpu(tcp.key->flags);
 
                if (tcp_flags & ~NFP_FLOWER_SUPPORTED_TCPFLAGS)
                        return -EOPNOTSUPP;
@@ -347,12 +314,12 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
                 * space, thus we need to ensure we include a IPv4/IPv6 key
                 * layer if we have not done so already.
                 */
-               if (!key_basic)
+               if (!basic.key)
                        return -EOPNOTSUPP;
 
                if (!(key_layer & NFP_FLOWER_LAYER_IPV4) &&
                    !(key_layer & NFP_FLOWER_LAYER_IPV6)) {
-                       switch (key_basic->n_proto) {
+                       switch (basic.key->n_proto) {
                        case cpu_to_be16(ETH_P_IP):
                                key_layer |= NFP_FLOWER_LAYER_IPV4;
                                key_size += sizeof(struct nfp_flower_ipv4);
@@ -369,14 +336,11 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
                }
        }
 
-       if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
-               struct flow_dissector_key_control *key_ctl;
-
-               key_ctl = skb_flow_dissector_target(flow->dissector,
-                                                   FLOW_DISSECTOR_KEY_CONTROL,
-                                                   flow->key);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+               struct flow_match_control ctl;
 
-               if (key_ctl->flags & ~NFP_FLOWER_SUPPORTED_CTLFLAGS)
+               flow_rule_match_control(rule, &ctl);
+               if (ctl.key->flags & ~NFP_FLOWER_SUPPORTED_CTLFLAGS)
                        return -EOPNOTSUPP;
        }
 
@@ -589,9 +553,8 @@ nfp_flower_get_stats(struct nfp_app *app, struct net_device *netdev,
        ctx_id = be32_to_cpu(nfp_flow->meta.host_ctx_id);
 
        spin_lock_bh(&priv->stats_lock);
-       tcf_exts_stats_update(flow->exts, priv->stats[ctx_id].bytes,
-                             priv->stats[ctx_id].pkts,
-                             priv->stats[ctx_id].used);
+       flow_stats_update(&flow->stats, priv->stats[ctx_id].bytes,
+                         priv->stats[ctx_id].pkts, priv->stats[ctx_id].used);
 
        priv->stats[ctx_id].pkts = 0;
        priv->stats[ctx_id].bytes = 0;
index 808647e..db2da99 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/rtnetlink.h>
 #include <net/devlink.h>
 
+#include "nfpcore/nfp.h"
 #include "nfpcore/nfp_nsp.h"
 #include "nfp_app.h"
 #include "nfp_main.h"
@@ -171,6 +172,173 @@ static int nfp_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
        return ret;
 }
 
+static const struct nfp_devlink_versions_simple {
+       const char *key;
+       const char *hwinfo;
+} nfp_devlink_versions_hwinfo[] = {
+       { DEVLINK_INFO_VERSION_GENERIC_BOARD_ID,        "assembly.partno", },
+       { DEVLINK_INFO_VERSION_GENERIC_BOARD_REV,       "assembly.revision", },
+       { DEVLINK_INFO_VERSION_GENERIC_BOARD_MANUFACTURE, "assembly.vendor", },
+       { "board.model", /* code name */                "assembly.model", },
+};
+
+static int
+nfp_devlink_versions_get_hwinfo(struct nfp_pf *pf, struct devlink_info_req *req)
+{
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < ARRAY_SIZE(nfp_devlink_versions_hwinfo); i++) {
+               const struct nfp_devlink_versions_simple *info;
+               const char *val;
+
+               info = &nfp_devlink_versions_hwinfo[i];
+
+               val = nfp_hwinfo_lookup(pf->hwinfo, info->hwinfo);
+               if (!val)
+                       continue;
+
+               err = devlink_info_version_fixed_put(req, info->key, val);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static const struct nfp_devlink_versions {
+       enum nfp_nsp_versions id;
+       const char *key;
+} nfp_devlink_versions_nsp[] = {
+       { NFP_VERSIONS_BUNDLE,  "fw.bundle_id", },
+       { NFP_VERSIONS_BSP,     DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, },
+       { NFP_VERSIONS_CPLD,    "fw.cpld", },
+       { NFP_VERSIONS_APP,     DEVLINK_INFO_VERSION_GENERIC_FW_APP, },
+       { NFP_VERSIONS_UNDI,    DEVLINK_INFO_VERSION_GENERIC_FW_UNDI, },
+       { NFP_VERSIONS_NCSI,    DEVLINK_INFO_VERSION_GENERIC_FW_NCSI, },
+       { NFP_VERSIONS_CFGR,    "chip.init", },
+};
+
+static int
+nfp_devlink_versions_get_nsp(struct devlink_info_req *req, bool flash,
+                            const u8 *buf, unsigned int size)
+{
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < ARRAY_SIZE(nfp_devlink_versions_nsp); i++) {
+               const struct nfp_devlink_versions *info;
+               const char *version;
+
+               info = &nfp_devlink_versions_nsp[i];
+
+               version = nfp_nsp_versions_get(info->id, flash, buf, size);
+               if (IS_ERR(version)) {
+                       if (PTR_ERR(version) == -ENOENT)
+                               continue;
+                       else
+                               return PTR_ERR(version);
+               }
+
+               if (flash)
+                       err = devlink_info_version_stored_put(req, info->key,
+                                                             version);
+               else
+                       err = devlink_info_version_running_put(req, info->key,
+                                                              version);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int
+nfp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
+                    struct netlink_ext_ack *extack)
+{
+       struct nfp_pf *pf = devlink_priv(devlink);
+       const char *sn, *vendor, *part;
+       struct nfp_nsp *nsp;
+       char *buf = NULL;
+       int err;
+
+       err = devlink_info_driver_name_put(req, "nfp");
+       if (err)
+               return err;
+
+       vendor = nfp_hwinfo_lookup(pf->hwinfo, "assembly.vendor");
+       part = nfp_hwinfo_lookup(pf->hwinfo, "assembly.partno");
+       sn = nfp_hwinfo_lookup(pf->hwinfo, "assembly.serial");
+       if (vendor && part && sn) {
+               char *buf;
+
+               buf = kmalloc(strlen(vendor) + strlen(part) + strlen(sn) + 1,
+                             GFP_KERNEL);
+               if (!buf)
+                       return -ENOMEM;
+
+               buf[0] = '\0';
+               strcat(buf, vendor);
+               strcat(buf, part);
+               strcat(buf, sn);
+
+               err = devlink_info_serial_number_put(req, buf);
+               kfree(buf);
+               if (err)
+                       return err;
+       }
+
+       nsp = nfp_nsp_open(pf->cpp);
+       if (IS_ERR(nsp)) {
+               NL_SET_ERR_MSG_MOD(extack, "can't access NSP");
+               return PTR_ERR(nsp);
+       }
+
+       if (nfp_nsp_has_versions(nsp)) {
+               buf = kzalloc(NFP_NSP_VERSION_BUFSZ, GFP_KERNEL);
+               if (!buf) {
+                       err = -ENOMEM;
+                       goto err_close_nsp;
+               }
+
+               err = nfp_nsp_versions(nsp, buf, NFP_NSP_VERSION_BUFSZ);
+               if (err)
+                       goto err_free_buf;
+
+               err = nfp_devlink_versions_get_nsp(req, false,
+                                                  buf, NFP_NSP_VERSION_BUFSZ);
+               if (err)
+                       goto err_free_buf;
+
+               err = nfp_devlink_versions_get_nsp(req, true,
+                                                  buf, NFP_NSP_VERSION_BUFSZ);
+               if (err)
+                       goto err_free_buf;
+
+               kfree(buf);
+       }
+
+       nfp_nsp_close(nsp);
+
+       return nfp_devlink_versions_get_hwinfo(pf, req);
+
+err_free_buf:
+       kfree(buf);
+err_close_nsp:
+       nfp_nsp_close(nsp);
+       return err;
+}
+
+static int
+nfp_devlink_flash_update(struct devlink *devlink, const char *path,
+                        const char *component, struct netlink_ext_ack *extack)
+{
+       if (component)
+               return -EOPNOTSUPP;
+       return nfp_flash_update_common(devlink_priv(devlink), path, extack);
+}
+
 const struct devlink_ops nfp_devlink_ops = {
        .port_split             = nfp_devlink_port_split,
        .port_unsplit           = nfp_devlink_port_unsplit,
@@ -178,6 +346,8 @@ const struct devlink_ops nfp_devlink_ops = {
        .sb_pool_set            = nfp_devlink_sb_pool_set,
        .eswitch_mode_get       = nfp_devlink_eswitch_mode_get,
        .eswitch_mode_set       = nfp_devlink_eswitch_mode_set,
+       .info_get               = nfp_devlink_info_get,
+       .flash_update           = nfp_devlink_flash_update,
 };
 
 int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port)
index 6c10e8d..f4c8776 100644 (file)
@@ -300,6 +300,47 @@ static int nfp_pcie_sriov_configure(struct pci_dev *pdev, int num_vfs)
                return nfp_pcie_sriov_enable(pdev, num_vfs);
 }
 
+int nfp_flash_update_common(struct nfp_pf *pf, const char *path,
+                           struct netlink_ext_ack *extack)
+{
+       struct device *dev = &pf->pdev->dev;
+       const struct firmware *fw;
+       struct nfp_nsp *nsp;
+       int err;
+
+       nsp = nfp_nsp_open(pf->cpp);
+       if (IS_ERR(nsp)) {
+               err = PTR_ERR(nsp);
+               if (extack)
+                       NL_SET_ERR_MSG_MOD(extack, "can't access NSP");
+               else
+                       dev_err(dev, "Failed to access the NSP: %d\n", err);
+               return err;
+       }
+
+       err = request_firmware_direct(&fw, path, dev);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "unable to read flash file from disk");
+               goto exit_close_nsp;
+       }
+
+       dev_info(dev, "Please be patient while writing flash image: %s\n",
+                path);
+
+       err = nfp_nsp_write_flash(nsp, fw);
+       if (err < 0)
+               goto exit_release_fw;
+       dev_info(dev, "Finished writing flash image\n");
+       err = 0;
+
+exit_release_fw:
+       release_firmware(fw);
+exit_close_nsp:
+       nfp_nsp_close(nsp);
+       return err;
+}
+
 static const struct firmware *
 nfp_net_fw_request(struct pci_dev *pdev, struct nfp_pf *pf, const char *name)
 {
index a3613a2..b7211f2 100644 (file)
@@ -164,6 +164,8 @@ nfp_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt,
                 unsigned int min_size, struct nfp_cpp_area **area);
 int nfp_mbox_cmd(struct nfp_pf *pf, u32 cmd, void *in_data, u64 in_length,
                 void *out_data, u64 out_length);
+int nfp_flash_update_common(struct nfp_pf *pf, const char *path,
+                           struct netlink_ext_ack *extack);
 
 enum nfp_dump_diag {
        NFP_DUMP_NSP_DIAG = 0,
index 7d2d424..776f6c0 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/vmalloc.h>
 #include <linux/ktime.h>
 
-#include <net/switchdev.h>
 #include <net/vxlan.h>
 
 #include "nfpcore/nfp_nsp.h"
@@ -3531,6 +3530,7 @@ const struct net_device_ops nfp_net_netdev_ops = {
        .ndo_udp_tunnel_add     = nfp_net_add_vxlan_port,
        .ndo_udp_tunnel_del     = nfp_net_del_vxlan_port,
        .ndo_bpf                = nfp_net_xdp,
+       .ndo_get_port_parent_id = nfp_port_get_port_parent_id,
 };
 
 /**
@@ -3815,8 +3815,6 @@ static void nfp_net_netdev_init(struct nfp_net *nn)
        netdev->netdev_ops = &nfp_net_netdev_ops;
        netdev->watchdog_timeo = msecs_to_jiffies(5 * 1000);
 
-       SWITCHDEV_SET_OPS(netdev, &nfp_port_switchdev_ops);
-
        /* MTU range: 68 - hw-specific max */
        netdev->min_mtu = ETH_MIN_MTU;
        netdev->max_mtu = nn->max_mtu;
index cb9c512..8f18914 100644 (file)
@@ -1237,11 +1237,8 @@ static int nfp_net_set_channels(struct net_device *netdev,
 static int
 nfp_net_flash_device(struct net_device *netdev, struct ethtool_flash *flash)
 {
-       const struct firmware *fw;
        struct nfp_app *app;
-       struct nfp_nsp *nsp;
-       struct device *dev;
-       int err;
+       int ret;
 
        if (flash->region != ETHTOOL_FLASH_ALL_REGIONS)
                return -EOPNOTSUPP;
@@ -1250,39 +1247,13 @@ nfp_net_flash_device(struct net_device *netdev, struct ethtool_flash *flash)
        if (!app)
                return -EOPNOTSUPP;
 
-       dev = &app->pdev->dev;
-
-       nsp = nfp_nsp_open(app->cpp);
-       if (IS_ERR(nsp)) {
-               err = PTR_ERR(nsp);
-               dev_err(dev, "Failed to access the NSP: %d\n", err);
-               return err;
-       }
-
-       err = request_firmware_direct(&fw, flash->data, dev);
-       if (err)
-               goto exit_close_nsp;
-
-       dev_info(dev, "Please be patient while writing flash image: %s\n",
-                flash->data);
        dev_hold(netdev);
        rtnl_unlock();
-
-       err = nfp_nsp_write_flash(nsp, fw);
-       if (err < 0) {
-               dev_err(dev, "Flash write failed: %d\n", err);
-               goto exit_rtnl_lock;
-       }
-       dev_info(dev, "Finished writing flash image\n");
-
-exit_rtnl_lock:
+       ret = nfp_flash_update_common(app->pf, flash->data, NULL);
        rtnl_lock();
        dev_put(netdev);
-       release_firmware(fw);
 
-exit_close_nsp:
-       nfp_nsp_close(nsp);
-       return err;
+       return ret;
 }
 
 static const struct ethtool_ops nfp_net_ethtool_ops = {
index 69d7aeb..6283980 100644 (file)
@@ -5,7 +5,6 @@
 #include <linux/io-64-nonatomic-hi-lo.h>
 #include <linux/lockdep.h>
 #include <net/dst_metadata.h>
-#include <net/switchdev.h>
 
 #include "nfpcore/nfp_cpp.h"
 #include "nfpcore/nfp_nsp.h"
@@ -273,6 +272,7 @@ const struct net_device_ops nfp_repr_netdev_ops = {
        .ndo_fix_features       = nfp_repr_fix_features,
        .ndo_set_features       = nfp_port_set_features,
        .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_get_port_parent_id = nfp_port_get_port_parent_id,
 };
 
 void
@@ -336,8 +336,6 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
 
        netdev->max_mtu = pf_netdev->max_mtu;
 
-       SWITCHDEV_SET_OPS(netdev, &nfp_port_switchdev_ops);
-
        /* Set features the lower device can support with representors */
        if (repr_cap & NFP_NET_CFG_CTRL_LIVE_ADDR)
                netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
index 86bc149..7e90880 100644 (file)
@@ -31,34 +31,22 @@ struct nfp_port *nfp_port_from_netdev(struct net_device *netdev)
        return NULL;
 }
 
-static int
-nfp_port_attr_get(struct net_device *netdev, struct switchdev_attr *attr)
+int nfp_port_get_port_parent_id(struct net_device *netdev,
+                               struct netdev_phys_item_id *ppid)
 {
        struct nfp_port *port;
+       const u8 *serial;
 
        port = nfp_port_from_netdev(netdev);
        if (!port)
                return -EOPNOTSUPP;
 
-       switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: {
-               const u8 *serial;
-               /* N.B: attr->u.ppid.id is binary data */
-               attr->u.ppid.id_len = nfp_cpp_serial(port->app->cpp, &serial);
-               memcpy(&attr->u.ppid.id, serial, attr->u.ppid.id_len);
-               break;
-       }
-       default:
-               return -EOPNOTSUPP;
-       }
+       ppid->id_len = nfp_cpp_serial(port->app->cpp, &serial);
+       memcpy(&ppid->id, serial, ppid->id_len);
 
        return 0;
 }
 
-const struct switchdev_ops nfp_port_switchdev_ops = {
-       .switchdev_port_attr_get        = nfp_port_attr_get,
-};
-
 int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type,
                      void *type_data)
 {
index b2479a2..90ae053 100644 (file)
@@ -7,6 +7,7 @@
 #include <net/devlink.h>
 
 struct net_device;
+struct netdev_phys_item_id;
 struct nfp_app;
 struct nfp_pf;
 struct nfp_port;
@@ -90,7 +91,6 @@ struct nfp_port {
 };
 
 extern const struct ethtool_ops nfp_port_ethtool_ops;
-extern const struct switchdev_ops nfp_port_switchdev_ops;
 
 __printf(2, 3) u8 *nfp_pr_et(u8 *data, const char *fmt, ...);
 
@@ -106,6 +106,8 @@ int
 nfp_port_set_features(struct net_device *netdev, netdev_features_t features);
 
 struct nfp_port *nfp_port_from_netdev(struct net_device *netdev);
+int nfp_port_get_port_parent_id(struct net_device *netdev,
+                               struct netdev_phys_item_id *ppid);
 struct nfp_port *
 nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id);
 struct nfp_eth_table_port *__nfp_port_get_eth_port(struct nfp_port *port);
index 814360e..ea2e3f8 100644 (file)
@@ -48,6 +48,7 @@ int nfp_shared_buf_pool_get(struct nfp_pf *pf, unsigned int sb, u16 pool_index,
        pool_info->pool_type = le32_to_cpu(get_data.pool_type);
        pool_info->threshold_type = le32_to_cpu(get_data.threshold_type);
        pool_info->size = le32_to_cpu(get_data.size) * unit_size;
+       pool_info->cell_size = unit_size;
 
        return 0;
 }
index ce1577b..a9d53df 100644 (file)
@@ -7,6 +7,7 @@
  *         Jason McMullan <jason.mcmullan@netronome.com>
  */
 
+#include <asm/unaligned.h>
 #include <linux/bitfield.h>
 #include <linux/delay.h>
 #include <linux/firmware.h>
 
 #define NFP_HWINFO_LOOKUP_SIZE GENMASK(11, 0)
 
+#define NFP_VERSIONS_SIZE      GENMASK(11, 0)
+#define NFP_VERSIONS_CNT_OFF   0
+#define NFP_VERSIONS_BSP_OFF   2
+#define NFP_VERSIONS_CPLD_OFF  6
+#define NFP_VERSIONS_APP_OFF   10
+#define NFP_VERSIONS_BUNDLE_OFF        14
+#define NFP_VERSIONS_UNDI_OFF  18
+#define NFP_VERSIONS_NCSI_OFF  22
+#define NFP_VERSIONS_CFGR_OFF  26
+
 enum nfp_nsp_cmd {
        SPCODE_NOOP             = 0, /* No operation */
        SPCODE_SOFT_RESET       = 1, /* Soft reset the NFP */
@@ -77,6 +88,7 @@ enum nfp_nsp_cmd {
        SPCODE_NSP_IDENTIFY     = 13, /* Read NSP version */
        SPCODE_FW_STORED        = 16, /* If no FW loaded, load flash app FW */
        SPCODE_HWINFO_LOOKUP    = 17, /* Lookup HWinfo with overwrites etc. */
+       SPCODE_VERSIONS         = 21, /* Report FW versions */
 };
 
 static const struct {
@@ -711,3 +723,52 @@ int nfp_nsp_hwinfo_lookup(struct nfp_nsp *state, void *buf, unsigned int size)
 
        return 0;
 }
+
+int nfp_nsp_versions(struct nfp_nsp *state, void *buf, unsigned int size)
+{
+       struct nfp_nsp_command_buf_arg versions = {
+               {
+                       .code           = SPCODE_VERSIONS,
+                       .option         = min_t(u32, size, NFP_VERSIONS_SIZE),
+               },
+               .out_buf        = buf,
+               .out_size       = min_t(u32, size, NFP_VERSIONS_SIZE),
+       };
+
+       return nfp_nsp_command_buf(state, &versions);
+}
+
+const char *nfp_nsp_versions_get(enum nfp_nsp_versions id, bool flash,
+                                const u8 *buf, unsigned int size)
+{
+       static const u32 id2off[] = {
+               [NFP_VERSIONS_BSP] =    NFP_VERSIONS_BSP_OFF,
+               [NFP_VERSIONS_CPLD] =   NFP_VERSIONS_CPLD_OFF,
+               [NFP_VERSIONS_APP] =    NFP_VERSIONS_APP_OFF,
+               [NFP_VERSIONS_BUNDLE] = NFP_VERSIONS_BUNDLE_OFF,
+               [NFP_VERSIONS_UNDI] =   NFP_VERSIONS_UNDI_OFF,
+               [NFP_VERSIONS_NCSI] =   NFP_VERSIONS_NCSI_OFF,
+               [NFP_VERSIONS_CFGR] =   NFP_VERSIONS_CFGR_OFF,
+       };
+       unsigned int field, buf_field_cnt, buf_off;
+
+       if (id >= ARRAY_SIZE(id2off) || !id2off[id])
+               return ERR_PTR(-EINVAL);
+
+       field = id * 2 + flash;
+
+       buf_field_cnt = get_unaligned_le16(buf);
+       if (buf_field_cnt <= field)
+               return ERR_PTR(-ENOENT);
+
+       buf_off = get_unaligned_le16(buf + id2off[id] + flash * 2);
+       if (!buf_off)
+               return ERR_PTR(-ENOENT);
+
+       if (buf_off >= size)
+               return ERR_PTR(-EINVAL);
+       if (strnlen(&buf[buf_off], size - buf_off) == size - buf_off)
+               return ERR_PTR(-EINVAL);
+
+       return (const char *)&buf[buf_off];
+}
index ff33ac5..246e213 100644 (file)
@@ -38,6 +38,11 @@ static inline bool nfp_nsp_has_hwinfo_lookup(struct nfp_nsp *state)
        return nfp_nsp_get_abi_ver_minor(state) > 24;
 }
 
+static inline bool nfp_nsp_has_versions(struct nfp_nsp *state)
+{
+       return nfp_nsp_get_abi_ver_minor(state) > 27;
+}
+
 enum nfp_eth_interface {
        NFP_INTERFACE_NONE      = 0,
        NFP_INTERFACE_SFP       = 1,
@@ -208,4 +213,19 @@ enum nfp_nsp_sensor_id {
 int nfp_hwmon_read_sensor(struct nfp_cpp *cpp, enum nfp_nsp_sensor_id id,
                          long *val);
 
+#define NFP_NSP_VERSION_BUFSZ  1024 /* reasonable size, not in the ABI */
+
+enum nfp_nsp_versions {
+       NFP_VERSIONS_BSP,
+       NFP_VERSIONS_CPLD,
+       NFP_VERSIONS_APP,
+       NFP_VERSIONS_BUNDLE,
+       NFP_VERSIONS_UNDI,
+       NFP_VERSIONS_NCSI,
+       NFP_VERSIONS_CFGR,
+};
+
+int nfp_nsp_versions(struct nfp_nsp *state, void *buf, unsigned int size);
+const char *nfp_nsp_versions_get(enum nfp_nsp_versions id, bool flash,
+                                const u8 *buf, unsigned int size);
 #endif
index 802c922..f6f028f 100644 (file)
@@ -269,8 +269,7 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp)
                goto err;
        }
 
-       table = kzalloc(sizeof(*table) +
-                       sizeof(struct nfp_eth_table_port) * cnt, GFP_KERNEL);
+       table = kzalloc(struct_size(table, ports, cnt), GFP_KERNEL);
        if (!table)
                goto err;
 
index 1e408d1..96f7a98 100644 (file)
 #define NIXGE_MAX_JUMBO_FRAME_SIZE \
        (NIXGE_JUMBO_MTU + NIXGE_HDR_SIZE + NIXGE_TRL_SIZE)
 
+enum nixge_version {
+       NIXGE_V2,
+       NIXGE_V3,
+       NIXGE_VERSION_COUNT
+};
+
 struct nixge_hw_dma_bd {
        u32 next_lo;
        u32 next_hi;
@@ -1225,11 +1231,60 @@ static void *nixge_get_nvmem_address(struct device *dev)
        return mac;
 }
 
+/* Match table for of_platform binding */
+static const struct of_device_id nixge_dt_ids[] = {
+       { .compatible = "ni,xge-enet-2.00", .data = (void *)NIXGE_V2 },
+       { .compatible = "ni,xge-enet-3.00", .data = (void *)NIXGE_V3 },
+       {},
+};
+MODULE_DEVICE_TABLE(of, nixge_dt_ids);
+
+static int nixge_of_get_resources(struct platform_device *pdev)
+{
+       const struct of_device_id *of_id;
+       enum nixge_version version;
+       struct resource *ctrlres;
+       struct resource *dmares;
+       struct net_device *ndev;
+       struct nixge_priv *priv;
+
+       ndev = platform_get_drvdata(pdev);
+       priv = netdev_priv(ndev);
+       of_id = of_match_node(nixge_dt_ids, pdev->dev.of_node);
+       if (!of_id)
+               return -ENODEV;
+
+       version = (enum nixge_version)of_id->data;
+       if (version <= NIXGE_V2)
+               dmares = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       else
+               dmares = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                     "dma");
+
+       priv->dma_regs = devm_ioremap_resource(&pdev->dev, dmares);
+       if (IS_ERR(priv->dma_regs)) {
+               netdev_err(ndev, "failed to map dma regs\n");
+               return PTR_ERR(priv->dma_regs);
+       }
+       if (version <= NIXGE_V2) {
+               priv->ctrl_regs = priv->dma_regs + NIXGE_REG_CTRL_OFFSET;
+       } else {
+               ctrlres = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                      "ctrl");
+               priv->ctrl_regs = devm_ioremap_resource(&pdev->dev, ctrlres);
+       }
+       if (IS_ERR(priv->ctrl_regs)) {
+               netdev_err(ndev, "failed to map ctrl regs\n");
+               return PTR_ERR(priv->ctrl_regs);
+       }
+       return 0;
+}
+
 static int nixge_probe(struct platform_device *pdev)
 {
+       struct device_node *mn, *phy_node;
        struct nixge_priv *priv;
        struct net_device *ndev;
-       struct resource *dmares;
        const u8 *mac_addr;
        int err;
 
@@ -1261,14 +1316,9 @@ static int nixge_probe(struct platform_device *pdev)
        priv->dev = &pdev->dev;
 
        netif_napi_add(ndev, &priv->napi, nixge_poll, NAPI_POLL_WEIGHT);
-
-       dmares = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       priv->dma_regs = devm_ioremap_resource(&pdev->dev, dmares);
-       if (IS_ERR(priv->dma_regs)) {
-               netdev_err(ndev, "failed to map dma regs\n");
-               return PTR_ERR(priv->dma_regs);
-       }
-       priv->ctrl_regs = priv->dma_regs + NIXGE_REG_CTRL_OFFSET;
+       err = nixge_of_get_resources(pdev);
+       if (err)
+               return err;
        __nixge_hw_set_mac_address(ndev);
 
        priv->tx_irq = platform_get_irq_byname(pdev, "tx");
@@ -1286,10 +1336,14 @@ static int nixge_probe(struct platform_device *pdev)
        priv->coalesce_count_rx = XAXIDMA_DFT_RX_THRESHOLD;
        priv->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD;
 
-       err = nixge_mdio_setup(priv, pdev->dev.of_node);
-       if (err) {
-               netdev_err(ndev, "error registering mdio bus");
-               goto free_netdev;
+       mn = of_get_child_by_name(pdev->dev.of_node, "mdio");
+       if (mn) {
+               err = nixge_mdio_setup(priv, mn);
+               of_node_put(mn);
+               if (err) {
+                       netdev_err(ndev, "error registering mdio bus");
+                       goto free_netdev;
+               }
        }
 
        priv->phy_mode = of_get_phy_mode(pdev->dev.of_node);
@@ -1299,23 +1353,33 @@ static int nixge_probe(struct platform_device *pdev)
                goto unregister_mdio;
        }
 
-       priv->phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
-       if (!priv->phy_node) {
-               netdev_err(ndev, "not find \"phy-handle\" property\n");
-               err = -EINVAL;
-               goto unregister_mdio;
+       phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
+       if (!phy_node && of_phy_is_fixed_link(pdev->dev.of_node)) {
+               err = of_phy_register_fixed_link(pdev->dev.of_node);
+               if (err < 0) {
+                       netdev_err(ndev, "broken fixed-link specification\n");
+                       goto unregister_mdio;
+               }
+               phy_node = of_node_get(pdev->dev.of_node);
        }
+       priv->phy_node = phy_node;
 
        err = register_netdev(priv->ndev);
        if (err) {
                netdev_err(ndev, "register_netdev() error (%i)\n", err);
-               goto unregister_mdio;
+               goto free_phy;
        }
 
        return 0;
 
+free_phy:
+       if (of_phy_is_fixed_link(pdev->dev.of_node))
+               of_phy_deregister_fixed_link(pdev->dev.of_node);
+       of_node_put(phy_node);
+
 unregister_mdio:
-       mdiobus_unregister(priv->mii_bus);
+       if (priv->mii_bus)
+               mdiobus_unregister(priv->mii_bus);
 
 free_netdev:
        free_netdev(ndev);
@@ -1330,20 +1394,18 @@ static int nixge_remove(struct platform_device *pdev)
 
        unregister_netdev(ndev);
 
-       mdiobus_unregister(priv->mii_bus);
+       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);
+
+       if (priv->mii_bus)
+               mdiobus_unregister(priv->mii_bus);
 
        free_netdev(ndev);
 
        return 0;
 }
 
-/* Match table for of_platform binding */
-static const struct of_device_id nixge_dt_ids[] = {
-       { .compatible = "ni,xge-enet-2.00", },
-       {},
-};
-MODULE_DEVICE_TABLE(of, nixge_dt_ids);
-
 static struct platform_driver nixge_driver = {
        .probe          = nixge_probe,
        .remove         = nixge_remove,
index c662c6f..67bf02b 100644 (file)
@@ -630,7 +630,7 @@ static int w90p910_ether_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        if (!(w90p910_send_frame(dev, skb->data, skb->len))) {
                ether->skb = skb;
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
                return 0;
        }
        return -EAGAIN;
index c9529c2..eee883a 100644 (file)
@@ -1337,7 +1337,7 @@ static irqreturn_t hamachi_interrupt(int irq, void *dev_instance)
                                                        leXX_to_cpu(hmp->tx_ring[entry].addr),
                                                        skb->len,
                                                        PCI_DMA_TODEVICE);
-                                               dev_kfree_skb_irq(skb);
+                                               dev_consume_skb_irq(skb);
                                                hmp->tx_skbuff[entry] = NULL;
                                        }
                                        hmp->tx_ring[entry].status_n_length = 0;
index 54224d1..6f8d658 100644 (file)
@@ -925,7 +925,7 @@ static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance)
                        /* Free the original skb. */
                        pci_unmap_single(yp->pci_dev, le32_to_cpu(yp->tx_ring[entry].addr),
                                skb->len, PCI_DMA_TODEVICE);
-                       dev_kfree_skb_irq(skb);
+                       dev_consume_skb_irq(skb);
                        yp->tx_skbuff[entry] = NULL;
                }
                if (yp->tx_full &&
@@ -983,7 +983,7 @@ static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance)
                                pci_unmap_single(yp->pci_dev,
                                        yp->tx_ring[entry<<1].addr, skb->len,
                                        PCI_DMA_TODEVICE);
-                               dev_kfree_skb_irq(skb);
+                               dev_consume_skb_irq(skb);
                                yp->tx_skbuff[entry] = 0;
                                /* Mark status as empty. */
                                yp->tx_status[entry].tx_errs = 0;
index 3b0955d..2d21c94 100644 (file)
@@ -53,7 +53,7 @@
 extern const struct qed_common_ops qed_common_ops_pass;
 
 #define QED_MAJOR_VERSION              8
-#define QED_MINOR_VERSION              33
+#define QED_MINOR_VERSION              37
 #define QED_REVISION_VERSION           0
 #define QED_ENGINEERING_VERSION                20
 
index 35c9f48..e61d1d9 100644 (file)
@@ -2135,12 +2135,12 @@ int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn, u32 rdma_tasks)
                struct qed_eth_pf_params *p_params =
                    &p_hwfn->pf_params.eth_pf_params;
 
-                       if (!p_params->num_vf_cons)
-                               p_params->num_vf_cons =
-                                   ETH_PF_PARAMS_VF_CONS_DEFAULT;
-                       qed_cxt_set_proto_cid_count(p_hwfn, PROTOCOLID_ETH,
-                                                   p_params->num_cons,
-                                                   p_params->num_vf_cons);
+               if (!p_params->num_vf_cons)
+                       p_params->num_vf_cons =
+                           ETH_PF_PARAMS_VF_CONS_DEFAULT;
+               qed_cxt_set_proto_cid_count(p_hwfn, PROTOCOLID_ETH,
+                                           p_params->num_cons,
+                                           p_params->num_vf_cons);
                p_hwfn->p_cxt_mngr->arfs_count = p_params->num_arfs_filters;
                break;
        }
index b17003d..e2cbd77 100644 (file)
@@ -795,19 +795,19 @@ static void qed_init_qm_pq(struct qed_hwfn *p_hwfn,
 
 /* get pq index according to PQ_FLAGS */
 static u16 *qed_init_qm_get_idx_from_flags(struct qed_hwfn *p_hwfn,
-                                          u32 pq_flags)
+                                          unsigned long pq_flags)
 {
        struct qed_qm_info *qm_info = &p_hwfn->qm_info;
 
        /* Can't have multiple flags set here */
-       if (bitmap_weight((unsigned long *)&pq_flags,
+       if (bitmap_weight(&pq_flags,
                          sizeof(pq_flags) * BITS_PER_BYTE) > 1) {
-               DP_ERR(p_hwfn, "requested multiple pq flags 0x%x\n", pq_flags);
+               DP_ERR(p_hwfn, "requested multiple pq flags 0x%lx\n", pq_flags);
                goto err;
        }
 
        if (!(qed_get_pq_flags(p_hwfn) & pq_flags)) {
-               DP_ERR(p_hwfn, "pq flag 0x%x is not set\n", pq_flags);
+               DP_ERR(p_hwfn, "pq flag 0x%lx is not set\n", pq_flags);
                goto err;
        }
 
index 417121e..37edaa8 100644 (file)
@@ -12796,6 +12796,7 @@ struct public_drv_mb {
 #define FW_MB_PARAM_GET_PF_RDMA_BOTH           0x3
 
 /* get MFW feature support response */
+#define FW_MB_PARAM_FEATURE_SUPPORT_SMARTLINQ  0x00000001
 #define FW_MB_PARAM_FEATURE_SUPPORT_EEE                0x00000002
 #define FW_MB_PARAM_FEATURE_SUPPORT_VLINK      0x00010000
 
index 67c02ea..58be1c4 100644 (file)
@@ -609,6 +609,10 @@ qed_sp_update_accept_mode(struct qed_hwfn *p_hwfn,
                          (!!(accept_filter & QED_ACCEPT_MCAST_MATCHED) &&
                           !!(accept_filter & QED_ACCEPT_MCAST_UNMATCHED)));
 
+               SET_FIELD(state, ETH_VPORT_TX_MODE_UCAST_ACCEPT_ALL,
+                         (!!(accept_filter & QED_ACCEPT_UCAST_MATCHED) &&
+                          !!(accept_filter & QED_ACCEPT_UCAST_UNMATCHED)));
+
                SET_FIELD(state, ETH_VPORT_TX_MODE_BCAST_ACCEPT_ALL,
                          !!(accept_filter & QED_ACCEPT_BCAST));
 
@@ -744,6 +748,11 @@ int qed_sp_vport_update(struct qed_hwfn *p_hwfn,
                return rc;
        }
 
+       if (p_params->update_ctl_frame_check) {
+               p_cmn->ctl_frame_mac_check_en = p_params->mac_chk_en;
+               p_cmn->ctl_frame_ethtype_check_en = p_params->ethtype_chk_en;
+       }
+
        /* Update mcast bins for VFs, PF doesn't use this functionality */
        qed_sp_update_mcast_bin(p_hwfn, p_ramrod, p_params);
 
@@ -2207,7 +2216,7 @@ static int qed_fill_eth_dev_info(struct qed_dev *cdev,
                        u16 num_queues = 0;
 
                        /* Since the feature controls only queue-zones,
-                        * make sure we have the contexts [rx, tx, xdp] to
+                        * make sure we have the contexts [rx, xdp, tcs] to
                         * match.
                         */
                        for_each_hwfn(cdev, i) {
@@ -2217,7 +2226,8 @@ static int qed_fill_eth_dev_info(struct qed_dev *cdev,
                                u16 cids;
 
                                cids = hwfn->pf_params.eth_pf_params.num_cons;
-                               num_queues += min_t(u16, l2_queues, cids / 3);
+                               cids /= (2 + info->num_tc);
+                               num_queues += min_t(u16, l2_queues, cids);
                        }
 
                        /* queues might theoretically be >256, but interrupts'
@@ -2688,7 +2698,8 @@ static int qed_configure_filter_rx_mode(struct qed_dev *cdev,
        if (type == QED_FILTER_RX_MODE_TYPE_PROMISC) {
                accept_flags.rx_accept_filter |= QED_ACCEPT_UCAST_UNMATCHED |
                                                 QED_ACCEPT_MCAST_UNMATCHED;
-               accept_flags.tx_accept_filter |= QED_ACCEPT_MCAST_UNMATCHED;
+               accept_flags.tx_accept_filter |= QED_ACCEPT_UCAST_UNMATCHED |
+                                                QED_ACCEPT_MCAST_UNMATCHED;
        } else if (type == QED_FILTER_RX_MODE_TYPE_MULTI_PROMISC) {
                accept_flags.rx_accept_filter |= QED_ACCEPT_MCAST_UNMATCHED;
                accept_flags.tx_accept_filter |= QED_ACCEPT_MCAST_UNMATCHED;
@@ -2860,7 +2871,8 @@ static int qed_get_coalesce(struct qed_dev *cdev, u16 *coal, void *handle)
        p_hwfn = p_cid->p_owner;
        rc = qed_get_queue_coalesce(p_hwfn, coal, handle);
        if (rc)
-               DP_NOTICE(p_hwfn, "Unable to read queue coalescing\n");
+               DP_VERBOSE(cdev, QED_MSG_DEBUG,
+                          "Unable to read queue coalescing\n");
 
        return rc;
 }
index 8d80f10..7127d5a 100644 (file)
@@ -219,6 +219,9 @@ struct qed_sp_vport_update_params {
        struct qed_rss_params           *rss_params;
        struct qed_filter_accept_flags  accept_flags;
        struct qed_sge_tpa_params       *sge_tpa_params;
+       u8                              update_ctl_frame_check;
+       u8                              mac_chk_en;
+       u8                              ethtype_chk_en;
 };
 
 int qed_sp_vport_update(struct qed_hwfn *p_hwfn,
index d9237c6..b5f419b 100644 (file)
@@ -2451,19 +2451,24 @@ static int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb,
 {
        struct qed_ll2_tx_pkt_info pkt;
        const skb_frag_t *frag;
+       u8 flags = 0, nr_frags;
        int rc = -EINVAL, i;
        dma_addr_t mapping;
        u16 vlan = 0;
-       u8 flags = 0;
 
        if (unlikely(skb->ip_summed != CHECKSUM_NONE)) {
                DP_INFO(cdev, "Cannot transmit a checksummed packet\n");
                return -EINVAL;
        }
 
-       if (1 + skb_shinfo(skb)->nr_frags > CORE_LL2_TX_MAX_BDS_PER_PACKET) {
+       /* Cache number of fragments from SKB since SKB may be freed by
+        * the completion routine after calling qed_ll2_prepare_tx_packet()
+        */
+       nr_frags = skb_shinfo(skb)->nr_frags;
+
+       if (1 + nr_frags > CORE_LL2_TX_MAX_BDS_PER_PACKET) {
                DP_ERR(cdev, "Cannot transmit a packet with %d fragments\n",
-                      1 + skb_shinfo(skb)->nr_frags);
+                      1 + nr_frags);
                return -EINVAL;
        }
 
@@ -2485,7 +2490,7 @@ static int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb,
        }
 
        memset(&pkt, 0, sizeof(pkt));
-       pkt.num_of_bds = 1 + skb_shinfo(skb)->nr_frags;
+       pkt.num_of_bds = 1 + nr_frags;
        pkt.vlan = vlan;
        pkt.bd_flags = flags;
        pkt.tx_dest = QED_LL2_TX_DEST_NW;
@@ -2496,12 +2501,17 @@ static int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb,
            test_bit(QED_LL2_XMIT_FLAGS_FIP_DISCOVERY, &xmit_flags))
                pkt.remove_stag = true;
 
+       /* qed_ll2_prepare_tx_packet() may actually send the packet if
+        * there are no fragments in the skb and subsequently the completion
+        * routine may run and free the SKB, so no dereferencing the SKB
+        * beyond this point unless skb has any fragments.
+        */
        rc = qed_ll2_prepare_tx_packet(&cdev->hwfns[0], cdev->ll2->handle,
                                       &pkt, 1);
        if (rc)
                goto err;
 
-       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+       for (i = 0; i < nr_frags; i++) {
                frag = &skb_shinfo(skb)->frags[i];
 
                mapping = skb_frag_dma_map(&cdev->pdev->dev, frag, 0,
index b473526..f164d4a 100644 (file)
@@ -281,6 +281,8 @@ int qed_fill_dev_info(struct qed_dev *cdev,
                if (hw_info->b_wol_support == QED_WOL_SUPPORT_PME)
                        dev_info->wol_support = true;
 
+               dev_info->smart_an = qed_mcp_is_smart_an_supported(p_hwfn);
+
                dev_info->abs_pf_id = QED_LEADING_HWFN(cdev)->abs_pf_id;
        } else {
                qed_vf_get_fw_version(&cdev->hwfns[0], &dev_info->fw_major,
index bb85418..cc27fd6 100644 (file)
@@ -3654,6 +3654,12 @@ void qed_mcp_resc_lock_default_init(struct qed_resc_lock_params *p_lock,
        }
 }
 
+bool qed_mcp_is_smart_an_supported(struct qed_hwfn *p_hwfn)
+{
+       return !!(p_hwfn->mcp_info->capabilities &
+                 FW_MB_PARAM_FEATURE_SUPPORT_SMARTLINQ);
+}
+
 int qed_mcp_get_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
 {
        u32 mcp_resp;
index 6e1d72a..2799e67 100644 (file)
@@ -1148,6 +1148,16 @@ void qed_mcp_resc_lock_default_init(struct qed_resc_lock_params *p_lock,
                                    struct qed_resc_unlock_params *p_unlock,
                                    enum qed_resc_lock
                                    resource, bool b_is_permanent);
+
+/**
+ * @brief - Return whether management firmware support smart AN
+ *
+ * @param p_hwfn
+ *
+ * @return bool - true if feature is supported.
+ */
+bool qed_mcp_is_smart_an_supported(struct qed_hwfn *p_hwfn);
+
 /**
  * @brief Learn of supported MFW features; To be done during early init
  *
index 4179c90..96ab77a 100644 (file)
@@ -382,6 +382,7 @@ void qed_consq_setup(struct qed_hwfn *p_hwfn);
  * @param p_hwfn
  */
 void qed_consq_free(struct qed_hwfn *p_hwfn);
+int qed_spq_pend_post(struct qed_hwfn *p_hwfn);
 
 /**
  * @file
index 888274f..5a495fd 100644 (file)
@@ -604,6 +604,9 @@ int qed_sp_pf_update_stag(struct qed_hwfn *p_hwfn)
 
        p_ent->ramrod.pf_update.update_mf_vlan_flag = true;
        p_ent->ramrod.pf_update.mf_vlan = cpu_to_le16(p_hwfn->hw_info.ovlan);
+       if (test_bit(QED_MF_UFP_SPECIFIC, &p_hwfn->cdev->mf_bits))
+               p_ent->ramrod.pf_update.mf_vlan |=
+                       cpu_to_le16(((u16)p_hwfn->ufp_info.tc << 13));
 
        return qed_spq_post(p_hwfn, p_ent, NULL);
 }
index 3e0f7c4..79b311b 100644 (file)
@@ -397,6 +397,11 @@ int qed_eq_completion(struct qed_hwfn *p_hwfn, void *cookie)
 
        qed_eq_prod_update(p_hwfn, qed_chain_get_prod_idx(p_chain));
 
+       /* Attempt to post pending requests */
+       spin_lock_bh(&p_hwfn->p_spq->lock);
+       rc = qed_spq_pend_post(p_hwfn);
+       spin_unlock_bh(&p_hwfn->p_spq->lock);
+
        return rc;
 }
 
@@ -767,7 +772,7 @@ static int qed_spq_post_list(struct qed_hwfn *p_hwfn,
        return 0;
 }
 
-static int qed_spq_pend_post(struct qed_hwfn *p_hwfn)
+int qed_spq_pend_post(struct qed_hwfn *p_hwfn)
 {
        struct qed_spq *p_spq = p_hwfn->p_spq;
        struct qed_spq_entry *p_ent = NULL;
@@ -927,7 +932,6 @@ int qed_spq_completion(struct qed_hwfn *p_hwfn,
        struct qed_spq_entry    *p_ent = NULL;
        struct qed_spq_entry    *tmp;
        struct qed_spq_entry    *found = NULL;
-       int                     rc;
 
        if (!p_hwfn)
                return -EINVAL;
@@ -985,12 +989,7 @@ int qed_spq_completion(struct qed_hwfn *p_hwfn,
                 */
                qed_spq_return_entry(p_hwfn, found);
 
-       /* Attempt to post pending requests */
-       spin_lock_bh(&p_spq->lock);
-       rc = qed_spq_pend_post(p_hwfn);
-       spin_unlock_bh(&p_spq->lock);
-
-       return rc;
+       return 0;
 }
 
 int qed_consq_alloc(struct qed_hwfn *p_hwfn)
index 71e28be..9faaa6d 100644 (file)
@@ -1969,7 +1969,9 @@ static void qed_iov_vf_mbx_start_vport(struct qed_hwfn *p_hwfn,
        params.vport_id = vf->vport_id;
        params.max_buffers_per_cqe = start->max_buffers_per_cqe;
        params.mtu = vf->mtu;
-       params.check_mac = true;
+
+       /* Non trusted VFs should enable control frame filtering */
+       params.check_mac = !vf->p_vf_info.is_trusted_configured;
 
        rc = qed_sp_eth_vport_start(p_hwfn, &params);
        if (rc) {
@@ -5137,6 +5139,9 @@ static void qed_iov_handle_trust_change(struct qed_hwfn *hwfn)
                params.opaque_fid = vf->opaque_fid;
                params.vport_id = vf->vport_id;
 
+               params.update_ctl_frame_check = 1;
+               params.mac_chk_en = !vf_info->is_trusted_configured;
+
                if (vf_info->rx_accept_mode & mask) {
                        flags->update_rx_mode_config = 1;
                        flags->rx_accept_filter = vf_info->rx_accept_mode;
@@ -5154,7 +5159,8 @@ static void qed_iov_handle_trust_change(struct qed_hwfn *hwfn)
                }
 
                if (flags->update_rx_mode_config ||
-                   flags->update_tx_mode_config)
+                   flags->update_tx_mode_config ||
+                   params.update_ctl_frame_check)
                        qed_sp_vport_update(hwfn, &params,
                                            QED_SPQ_MODE_EBLOCK, NULL);
        }
index b6cccf4..5dda547 100644 (file)
@@ -261,6 +261,7 @@ static int qed_vf_pf_acquire(struct qed_hwfn *p_hwfn)
        struct pfvf_acquire_resp_tlv *resp = &p_iov->pf2vf_reply->acquire_resp;
        struct pf_vf_pfdev_info *pfdev_info = &resp->pfdev_info;
        struct vf_pf_resc_request *p_resc;
+       u8 retry_cnt = VF_ACQUIRE_THRESH;
        bool resources_acquired = false;
        struct vfpf_acquire_tlv *req;
        int rc = 0, attempts = 0;
@@ -314,6 +315,15 @@ static int qed_vf_pf_acquire(struct qed_hwfn *p_hwfn)
 
                /* send acquire request */
                rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp));
+
+               /* Re-try acquire in case of vf-pf hw channel timeout */
+               if (retry_cnt && rc == -EBUSY) {
+                       DP_VERBOSE(p_hwfn, QED_MSG_IOV,
+                                  "VF retrying to acquire due to VPC timeout\n");
+                       retry_cnt--;
+                       continue;
+               }
+
                if (rc)
                        goto exit;
 
index 8434164..63a7816 100644 (file)
@@ -56,7 +56,7 @@
 #include <net/tc_act/tc_gact.h>
 
 #define QEDE_MAJOR_VERSION             8
-#define QEDE_MINOR_VERSION             33
+#define QEDE_MINOR_VERSION             37
 #define QEDE_REVISION_VERSION          0
 #define QEDE_ENGINEERING_VERSION       20
 #define DRV_MODULE_VERSION __stringify(QEDE_MAJOR_VERSION) "." \
@@ -497,6 +497,9 @@ struct qede_reload_args {
 
 /* Datapath functions definition */
 netdev_tx_t qede_start_xmit(struct sk_buff *skb, struct net_device *ndev);
+u16 qede_select_queue(struct net_device *dev, struct sk_buff *skb,
+                     struct net_device *sb_dev,
+                     select_queue_fallback_t fallback);
 netdev_features_t qede_features_check(struct sk_buff *skb,
                                      struct net_device *dev,
                                      netdev_features_t features);
index 16331c6..c623808 100644 (file)
@@ -186,11 +186,13 @@ static const struct {
 
 enum {
        QEDE_PRI_FLAG_CMT,
+       QEDE_PRI_FLAG_SMART_AN_SUPPORT, /* MFW supports SmartAN */
        QEDE_PRI_FLAG_LEN,
 };
 
 static const char qede_private_arr[QEDE_PRI_FLAG_LEN][ETH_GSTRING_LEN] = {
        "Coupled-Function",
+       "SmartAN capable",
 };
 
 enum qede_ethtool_tests {
@@ -404,8 +406,15 @@ static int qede_get_sset_count(struct net_device *dev, int stringset)
 static u32 qede_get_priv_flags(struct net_device *dev)
 {
        struct qede_dev *edev = netdev_priv(dev);
+       u32 flags = 0;
 
-       return (!!(edev->dev_info.common.num_hwfns > 1)) << QEDE_PRI_FLAG_CMT;
+       if (edev->dev_info.common.num_hwfns > 1)
+               flags |= BIT(QEDE_PRI_FLAG_CMT);
+
+       if (edev->dev_info.common.smart_an)
+               flags |= BIT(QEDE_PRI_FLAG_SMART_AN_SUPPORT);
+
+       return flags;
 }
 
 struct qede_link_mode_mapping {
index b16ce7d..add922b 100644 (file)
@@ -1665,198 +1665,6 @@ static int qede_set_v6_tuple_to_profile(struct qede_dev *edev,
        return 0;
 }
 
-static int qede_flow_spec_to_tuple_ipv4_common(struct qede_dev *edev,
-                                              struct qede_arfs_tuple *t,
-                                              struct ethtool_rx_flow_spec *fs)
-{
-       if ((fs->h_u.tcp_ip4_spec.ip4src &
-            fs->m_u.tcp_ip4_spec.ip4src) != fs->h_u.tcp_ip4_spec.ip4src) {
-               DP_INFO(edev, "Don't support IP-masks\n");
-               return -EOPNOTSUPP;
-       }
-
-       if ((fs->h_u.tcp_ip4_spec.ip4dst &
-            fs->m_u.tcp_ip4_spec.ip4dst) != fs->h_u.tcp_ip4_spec.ip4dst) {
-               DP_INFO(edev, "Don't support IP-masks\n");
-               return -EOPNOTSUPP;
-       }
-
-       if ((fs->h_u.tcp_ip4_spec.psrc &
-            fs->m_u.tcp_ip4_spec.psrc) != fs->h_u.tcp_ip4_spec.psrc) {
-               DP_INFO(edev, "Don't support port-masks\n");
-               return -EOPNOTSUPP;
-       }
-
-       if ((fs->h_u.tcp_ip4_spec.pdst &
-            fs->m_u.tcp_ip4_spec.pdst) != fs->h_u.tcp_ip4_spec.pdst) {
-               DP_INFO(edev, "Don't support port-masks\n");
-               return -EOPNOTSUPP;
-       }
-
-       if (fs->h_u.tcp_ip4_spec.tos) {
-               DP_INFO(edev, "Don't support tos\n");
-               return -EOPNOTSUPP;
-       }
-
-       t->eth_proto = htons(ETH_P_IP);
-       t->src_ipv4 = fs->h_u.tcp_ip4_spec.ip4src;
-       t->dst_ipv4 = fs->h_u.tcp_ip4_spec.ip4dst;
-       t->src_port = fs->h_u.tcp_ip4_spec.psrc;
-       t->dst_port = fs->h_u.tcp_ip4_spec.pdst;
-
-       return qede_set_v4_tuple_to_profile(edev, t);
-}
-
-static int qede_flow_spec_to_tuple_tcpv4(struct qede_dev *edev,
-                                        struct qede_arfs_tuple *t,
-                                        struct ethtool_rx_flow_spec *fs)
-{
-       t->ip_proto = IPPROTO_TCP;
-
-       if (qede_flow_spec_to_tuple_ipv4_common(edev, t, fs))
-               return -EINVAL;
-
-       return 0;
-}
-
-static int qede_flow_spec_to_tuple_udpv4(struct qede_dev *edev,
-                                        struct qede_arfs_tuple *t,
-                                        struct ethtool_rx_flow_spec *fs)
-{
-       t->ip_proto = IPPROTO_UDP;
-
-       if (qede_flow_spec_to_tuple_ipv4_common(edev, t, fs))
-               return -EINVAL;
-
-       return 0;
-}
-
-static int qede_flow_spec_to_tuple_ipv6_common(struct qede_dev *edev,
-                                              struct qede_arfs_tuple *t,
-                                              struct ethtool_rx_flow_spec *fs)
-{
-       struct in6_addr zero_addr;
-
-       memset(&zero_addr, 0, sizeof(zero_addr));
-
-       if ((fs->h_u.tcp_ip6_spec.psrc &
-            fs->m_u.tcp_ip6_spec.psrc) != fs->h_u.tcp_ip6_spec.psrc) {
-               DP_INFO(edev, "Don't support port-masks\n");
-               return -EOPNOTSUPP;
-       }
-
-       if ((fs->h_u.tcp_ip6_spec.pdst &
-            fs->m_u.tcp_ip6_spec.pdst) != fs->h_u.tcp_ip6_spec.pdst) {
-               DP_INFO(edev, "Don't support port-masks\n");
-               return -EOPNOTSUPP;
-       }
-
-       if (fs->h_u.tcp_ip6_spec.tclass) {
-               DP_INFO(edev, "Don't support tclass\n");
-               return -EOPNOTSUPP;
-       }
-
-       t->eth_proto = htons(ETH_P_IPV6);
-       memcpy(&t->src_ipv6, &fs->h_u.tcp_ip6_spec.ip6src,
-              sizeof(struct in6_addr));
-       memcpy(&t->dst_ipv6, &fs->h_u.tcp_ip6_spec.ip6dst,
-              sizeof(struct in6_addr));
-       t->src_port = fs->h_u.tcp_ip6_spec.psrc;
-       t->dst_port = fs->h_u.tcp_ip6_spec.pdst;
-
-       return qede_set_v6_tuple_to_profile(edev, t, &zero_addr);
-}
-
-static int qede_flow_spec_to_tuple_tcpv6(struct qede_dev *edev,
-                                        struct qede_arfs_tuple *t,
-                                        struct ethtool_rx_flow_spec *fs)
-{
-       t->ip_proto = IPPROTO_TCP;
-
-       if (qede_flow_spec_to_tuple_ipv6_common(edev, t, fs))
-               return -EINVAL;
-
-       return 0;
-}
-
-static int qede_flow_spec_to_tuple_udpv6(struct qede_dev *edev,
-                                        struct qede_arfs_tuple *t,
-                                        struct ethtool_rx_flow_spec *fs)
-{
-       t->ip_proto = IPPROTO_UDP;
-
-       if (qede_flow_spec_to_tuple_ipv6_common(edev, t, fs))
-               return -EINVAL;
-
-       return 0;
-}
-
-static int qede_flow_spec_to_tuple(struct qede_dev *edev,
-                                  struct qede_arfs_tuple *t,
-                                  struct ethtool_rx_flow_spec *fs)
-{
-       memset(t, 0, sizeof(*t));
-
-       if (qede_flow_spec_validate_unused(edev, fs))
-               return -EOPNOTSUPP;
-
-       switch ((fs->flow_type & ~FLOW_EXT)) {
-       case TCP_V4_FLOW:
-               return qede_flow_spec_to_tuple_tcpv4(edev, t, fs);
-       case UDP_V4_FLOW:
-               return qede_flow_spec_to_tuple_udpv4(edev, t, fs);
-       case TCP_V6_FLOW:
-               return qede_flow_spec_to_tuple_tcpv6(edev, t, fs);
-       case UDP_V6_FLOW:
-               return qede_flow_spec_to_tuple_udpv6(edev, t, fs);
-       default:
-               DP_VERBOSE(edev, NETIF_MSG_IFUP,
-                          "Can't support flow of type %08x\n", fs->flow_type);
-               return -EOPNOTSUPP;
-       }
-
-       return 0;
-}
-
-static int qede_flow_spec_validate(struct qede_dev *edev,
-                                  struct ethtool_rx_flow_spec *fs,
-                                  struct qede_arfs_tuple *t)
-{
-       if (fs->location >= QEDE_RFS_MAX_FLTR) {
-               DP_INFO(edev, "Location out-of-bounds\n");
-               return -EINVAL;
-       }
-
-       /* Check location isn't already in use */
-       if (test_bit(fs->location, edev->arfs->arfs_fltr_bmap)) {
-               DP_INFO(edev, "Location already in use\n");
-               return -EINVAL;
-       }
-
-       /* Check if the filtering-mode could support the filter */
-       if (edev->arfs->filter_count &&
-           edev->arfs->mode != t->mode) {
-               DP_INFO(edev,
-                       "flow_spec would require filtering mode %08x, but %08x is configured\n",
-                       t->mode, edev->arfs->filter_count);
-               return -EINVAL;
-       }
-
-       /* If drop requested then no need to validate other data */
-       if (fs->ring_cookie == RX_CLS_FLOW_DISC)
-               return 0;
-
-       if (ethtool_get_flow_spec_ring_vf(fs->ring_cookie))
-               return 0;
-
-       if (fs->ring_cookie >= QEDE_RSS_COUNT(edev)) {
-               DP_INFO(edev, "Queue out-of-bounds\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 /* Must be called while qede lock is held */
 static struct qede_arfs_fltr_node *
 qede_flow_find_fltr(struct qede_dev *edev, struct qede_arfs_tuple *t)
@@ -1896,72 +1704,6 @@ static void qede_flow_set_destination(struct qede_dev *edev,
                           "Configuring N-tuple for VF 0x%02x\n", n->vfid - 1);
 }
 
-int qede_add_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info)
-{
-       struct ethtool_rx_flow_spec *fsp = &info->fs;
-       struct qede_arfs_fltr_node *n;
-       struct qede_arfs_tuple t;
-       int min_hlen, rc;
-
-       __qede_lock(edev);
-
-       if (!edev->arfs) {
-               rc = -EPERM;
-               goto unlock;
-       }
-
-       /* Translate the flow specification into something fittign our DB */
-       rc = qede_flow_spec_to_tuple(edev, &t, fsp);
-       if (rc)
-               goto unlock;
-
-       /* Make sure location is valid and filter isn't already set */
-       rc = qede_flow_spec_validate(edev, fsp, &t);
-       if (rc)
-               goto unlock;
-
-       if (qede_flow_find_fltr(edev, &t)) {
-               rc = -EINVAL;
-               goto unlock;
-       }
-
-       n = kzalloc(sizeof(*n), GFP_KERNEL);
-       if (!n) {
-               rc = -ENOMEM;
-               goto unlock;
-       }
-
-       min_hlen = qede_flow_get_min_header_size(&t);
-       n->data = kzalloc(min_hlen, GFP_KERNEL);
-       if (!n->data) {
-               kfree(n);
-               rc = -ENOMEM;
-               goto unlock;
-       }
-
-       n->sw_id = fsp->location;
-       set_bit(n->sw_id, edev->arfs->arfs_fltr_bmap);
-       n->buf_len = min_hlen;
-
-       memcpy(&n->tuple, &t, sizeof(n->tuple));
-
-       qede_flow_set_destination(edev, n, fsp);
-
-       /* Build a minimal header according to the flow */
-       n->tuple.build_hdr(&n->tuple, n->data);
-
-       rc = qede_enqueue_fltr_and_config_searcher(edev, n, 0);
-       if (rc)
-               goto unlock;
-
-       qede_configure_arfs_fltr(edev, n, n->rxq_id, true);
-       rc = qede_poll_arfs_filter_config(edev, n);
-unlock:
-       __qede_unlock(edev);
-
-       return rc;
-}
-
 int qede_delete_flow_filter(struct qede_dev *edev, u64 cookie)
 {
        struct qede_arfs_fltr_node *fltr = NULL;
@@ -2004,190 +1746,172 @@ unlock:
 }
 
 static int qede_parse_actions(struct qede_dev *edev,
-                             struct tcf_exts *exts)
+                             struct flow_action *flow_action)
 {
-       int rc = -EINVAL, num_act = 0, i;
-       const struct tc_action *a;
-       bool is_drop = false;
+       const struct flow_action_entry *act;
+       int i;
 
-       if (!tcf_exts_has_actions(exts)) {
-               DP_NOTICE(edev, "No tc actions received\n");
-               return rc;
+       if (!flow_action_has_entries(flow_action)) {
+               DP_NOTICE(edev, "No actions received\n");
+               return -EINVAL;
        }
 
-       tcf_exts_for_each_action(i, a, exts) {
-               num_act++;
+       flow_action_for_each(i, act, flow_action) {
+               switch (act->id) {
+               case FLOW_ACTION_DROP:
+                       break;
+               case FLOW_ACTION_QUEUE:
+                       if (act->queue.vf)
+                               break;
 
-               if (is_tcf_gact_shot(a))
-                       is_drop = true;
+                       if (act->queue.index >= QEDE_RSS_COUNT(edev)) {
+                               DP_INFO(edev, "Queue out-of-bounds\n");
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       return -EINVAL;
+               }
        }
 
-       if (num_act == 1 && is_drop)
-               return 0;
-
-       return rc;
+       return 0;
 }
 
 static int
-qede_tc_parse_ports(struct qede_dev *edev,
-                   struct tc_cls_flower_offload *f,
-                   struct qede_arfs_tuple *t)
-{
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
-               struct flow_dissector_key_ports *key, *mask;
-
-               key = skb_flow_dissector_target(f->dissector,
-                                               FLOW_DISSECTOR_KEY_PORTS,
-                                               f->key);
-               mask = skb_flow_dissector_target(f->dissector,
-                                                FLOW_DISSECTOR_KEY_PORTS,
-                                                f->mask);
-
-               if ((key->src && mask->src != U16_MAX) ||
-                   (key->dst && mask->dst != U16_MAX)) {
+qede_flow_parse_ports(struct qede_dev *edev, struct flow_rule *rule,
+                     struct qede_arfs_tuple *t)
+{
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+               struct flow_match_ports match;
+
+               flow_rule_match_ports(rule, &match);
+               if ((match.key->src && match.mask->src != U16_MAX) ||
+                   (match.key->dst && match.mask->dst != U16_MAX)) {
                        DP_NOTICE(edev, "Do not support ports masks\n");
                        return -EINVAL;
                }
 
-               t->src_port = key->src;
-               t->dst_port = key->dst;
+               t->src_port = match.key->src;
+               t->dst_port = match.key->dst;
        }
 
        return 0;
 }
 
 static int
-qede_tc_parse_v6_common(struct qede_dev *edev,
-                       struct tc_cls_flower_offload *f,
-                       struct qede_arfs_tuple *t)
+qede_flow_parse_v6_common(struct qede_dev *edev, struct flow_rule *rule,
+                         struct qede_arfs_tuple *t)
 {
        struct in6_addr zero_addr, addr;
 
        memset(&zero_addr, 0, sizeof(addr));
        memset(&addr, 0xff, sizeof(addr));
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
-               struct flow_dissector_key_ipv6_addrs *key, *mask;
-
-               key = skb_flow_dissector_target(f->dissector,
-                                               FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                               f->key);
-               mask = skb_flow_dissector_target(f->dissector,
-                                                FLOW_DISSECTOR_KEY_IPV6_ADDRS,
-                                                f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
+               struct flow_match_ipv6_addrs match;
 
-               if ((memcmp(&key->src, &zero_addr, sizeof(addr)) &&
-                    memcmp(&mask->src, &addr, sizeof(addr))) ||
-                   (memcmp(&key->dst, &zero_addr, sizeof(addr)) &&
-                    memcmp(&mask->dst, &addr, sizeof(addr)))) {
+               flow_rule_match_ipv6_addrs(rule, &match);
+               if ((memcmp(&match.key->src, &zero_addr, sizeof(addr)) &&
+                    memcmp(&match.mask->src, &addr, sizeof(addr))) ||
+                   (memcmp(&match.key->dst, &zero_addr, sizeof(addr)) &&
+                    memcmp(&match.mask->dst, &addr, sizeof(addr)))) {
                        DP_NOTICE(edev,
                                  "Do not support IPv6 address prefix/mask\n");
                        return -EINVAL;
                }
 
-               memcpy(&t->src_ipv6, &key->src, sizeof(addr));
-               memcpy(&t->dst_ipv6, &key->dst, sizeof(addr));
+               memcpy(&t->src_ipv6, &match.key->src, sizeof(addr));
+               memcpy(&t->dst_ipv6, &match.key->dst, sizeof(addr));
        }
 
-       if (qede_tc_parse_ports(edev, f, t))
+       if (qede_flow_parse_ports(edev, rule, t))
                return -EINVAL;
 
        return qede_set_v6_tuple_to_profile(edev, t, &zero_addr);
 }
 
 static int
-qede_tc_parse_v4_common(struct qede_dev *edev,
-                       struct tc_cls_flower_offload *f,
+qede_flow_parse_v4_common(struct qede_dev *edev, struct flow_rule *rule,
                        struct qede_arfs_tuple *t)
 {
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
-               struct flow_dissector_key_ipv4_addrs *key, *mask;
-
-               key = skb_flow_dissector_target(f->dissector,
-                                               FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                               f->key);
-               mask = skb_flow_dissector_target(f->dissector,
-                                                FLOW_DISSECTOR_KEY_IPV4_ADDRS,
-                                                f->mask);
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
+               struct flow_match_ipv4_addrs match;
 
-               if ((key->src && mask->src != U32_MAX) ||
-                   (key->dst && mask->dst != U32_MAX)) {
+               flow_rule_match_ipv4_addrs(rule, &match);
+               if ((match.key->src && match.mask->src != U32_MAX) ||
+                   (match.key->dst && match.mask->dst != U32_MAX)) {
                        DP_NOTICE(edev, "Do not support ipv4 prefix/masks\n");
                        return -EINVAL;
                }
 
-               t->src_ipv4 = key->src;
-               t->dst_ipv4 = key->dst;
+               t->src_ipv4 = match.key->src;
+               t->dst_ipv4 = match.key->dst;
        }
 
-       if (qede_tc_parse_ports(edev, f, t))
+       if (qede_flow_parse_ports(edev, rule, t))
                return -EINVAL;
 
        return qede_set_v4_tuple_to_profile(edev, t);
 }
 
 static int
-qede_tc_parse_tcp_v6(struct qede_dev *edev,
-                    struct tc_cls_flower_offload *f,
+qede_flow_parse_tcp_v6(struct qede_dev *edev, struct flow_rule *rule,
                     struct qede_arfs_tuple *tuple)
 {
        tuple->ip_proto = IPPROTO_TCP;
        tuple->eth_proto = htons(ETH_P_IPV6);
 
-       return qede_tc_parse_v6_common(edev, f, tuple);
+       return qede_flow_parse_v6_common(edev, rule, tuple);
 }
 
 static int
-qede_tc_parse_tcp_v4(struct qede_dev *edev,
-                    struct tc_cls_flower_offload *f,
+qede_flow_parse_tcp_v4(struct qede_dev *edev, struct flow_rule *rule,
                     struct qede_arfs_tuple *tuple)
 {
        tuple->ip_proto = IPPROTO_TCP;
        tuple->eth_proto = htons(ETH_P_IP);
 
-       return qede_tc_parse_v4_common(edev, f, tuple);
+       return qede_flow_parse_v4_common(edev, rule, tuple);
 }
 
 static int
-qede_tc_parse_udp_v6(struct qede_dev *edev,
-                    struct tc_cls_flower_offload *f,
+qede_flow_parse_udp_v6(struct qede_dev *edev, struct flow_rule *rule,
                     struct qede_arfs_tuple *tuple)
 {
        tuple->ip_proto = IPPROTO_UDP;
        tuple->eth_proto = htons(ETH_P_IPV6);
 
-       return qede_tc_parse_v6_common(edev, f, tuple);
+       return qede_flow_parse_v6_common(edev, rule, tuple);
 }
 
 static int
-qede_tc_parse_udp_v4(struct qede_dev *edev,
-                    struct tc_cls_flower_offload *f,
+qede_flow_parse_udp_v4(struct qede_dev *edev, struct flow_rule *rule,
                     struct qede_arfs_tuple *tuple)
 {
        tuple->ip_proto = IPPROTO_UDP;
        tuple->eth_proto = htons(ETH_P_IP);
 
-       return qede_tc_parse_v4_common(edev, f, tuple);
+       return qede_flow_parse_v4_common(edev, rule, tuple);
 }
 
 static int
-qede_parse_flower_attr(struct qede_dev *edev, __be16 proto,
-                      struct tc_cls_flower_offload *f,
-                      struct qede_arfs_tuple *tuple)
+qede_parse_flow_attr(struct qede_dev *edev, __be16 proto,
+                    struct flow_rule *rule, struct qede_arfs_tuple *tuple)
 {
+       struct flow_dissector *dissector = rule->match.dissector;
        int rc = -EINVAL;
        u8 ip_proto = 0;
 
        memset(tuple, 0, sizeof(*tuple));
 
-       if (f->dissector->used_keys &
+       if (dissector->used_keys &
            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
              BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
              BIT(FLOW_DISSECTOR_KEY_BASIC) |
              BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
              BIT(FLOW_DISSECTOR_KEY_PORTS))) {
                DP_NOTICE(edev, "Unsupported key set:0x%x\n",
-                         f->dissector->used_keys);
+                         dissector->used_keys);
                return -EOPNOTSUPP;
        }
 
@@ -2197,25 +1921,23 @@ qede_parse_flower_attr(struct qede_dev *edev, __be16 proto,
                return -EPROTONOSUPPORT;
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *key;
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_match_basic match;
 
-               key = skb_flow_dissector_target(f->dissector,
-                                               FLOW_DISSECTOR_KEY_BASIC,
-                                               f->key);
-               ip_proto = key->ip_proto;
+               flow_rule_match_basic(rule, &match);
+               ip_proto = match.key->ip_proto;
        }
 
        if (ip_proto == IPPROTO_TCP && proto == htons(ETH_P_IP))
-               rc = qede_tc_parse_tcp_v4(edev, f, tuple);
+               rc = qede_flow_parse_tcp_v4(edev, rule, tuple);
        else if (ip_proto == IPPROTO_TCP && proto == htons(ETH_P_IPV6))
-               rc = qede_tc_parse_tcp_v6(edev, f, tuple);
+               rc = qede_flow_parse_tcp_v6(edev, rule, tuple);
        else if (ip_proto == IPPROTO_UDP && proto == htons(ETH_P_IP))
-               rc = qede_tc_parse_udp_v4(edev, f, tuple);
+               rc = qede_flow_parse_udp_v4(edev, rule, tuple);
        else if (ip_proto == IPPROTO_UDP && proto == htons(ETH_P_IPV6))
-               rc = qede_tc_parse_udp_v6(edev, f, tuple);
+               rc = qede_flow_parse_udp_v6(edev, rule, tuple);
        else
-               DP_NOTICE(edev, "Invalid tc protocol request\n");
+               DP_NOTICE(edev, "Invalid protocol request\n");
 
        return rc;
 }
@@ -2235,7 +1957,7 @@ int qede_add_tc_flower_fltr(struct qede_dev *edev, __be16 proto,
        }
 
        /* parse flower attribute and prepare filter */
-       if (qede_parse_flower_attr(edev, proto, f, &t))
+       if (qede_parse_flow_attr(edev, proto, f->rule, &t))
                goto unlock;
 
        /* Validate profile mode and number of filters */
@@ -2248,7 +1970,7 @@ int qede_add_tc_flower_fltr(struct qede_dev *edev, __be16 proto,
        }
 
        /* parse tc actions and get the vf_id */
-       if (qede_parse_actions(edev, f->exts))
+       if (qede_parse_actions(edev, &f->rule->action))
                goto unlock;
 
        if (qede_flow_find_fltr(edev, &t)) {
@@ -2290,3 +2012,141 @@ unlock:
        __qede_unlock(edev);
        return rc;
 }
+
+static int qede_flow_spec_validate(struct qede_dev *edev,
+                                  struct flow_action *flow_action,
+                                  struct qede_arfs_tuple *t,
+                                  __u32 location)
+{
+       if (location >= QEDE_RFS_MAX_FLTR) {
+               DP_INFO(edev, "Location out-of-bounds\n");
+               return -EINVAL;
+       }
+
+       /* Check location isn't already in use */
+       if (test_bit(location, edev->arfs->arfs_fltr_bmap)) {
+               DP_INFO(edev, "Location already in use\n");
+               return -EINVAL;
+       }
+
+       /* Check if the filtering-mode could support the filter */
+       if (edev->arfs->filter_count &&
+           edev->arfs->mode != t->mode) {
+               DP_INFO(edev,
+                       "flow_spec would require filtering mode %08x, but %08x is configured\n",
+                       t->mode, edev->arfs->filter_count);
+               return -EINVAL;
+       }
+
+       if (qede_parse_actions(edev, flow_action))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int qede_flow_spec_to_rule(struct qede_dev *edev,
+                                 struct qede_arfs_tuple *t,
+                                 struct ethtool_rx_flow_spec *fs)
+{
+       struct ethtool_rx_flow_spec_input input = {};
+       struct ethtool_rx_flow_rule *flow;
+       __be16 proto;
+       int err = 0;
+
+       if (qede_flow_spec_validate_unused(edev, fs))
+               return -EOPNOTSUPP;
+
+       switch ((fs->flow_type & ~FLOW_EXT)) {
+       case TCP_V4_FLOW:
+       case UDP_V4_FLOW:
+               proto = htons(ETH_P_IP);
+               break;
+       case TCP_V6_FLOW:
+       case UDP_V6_FLOW:
+               proto = htons(ETH_P_IPV6);
+               break;
+       default:
+               DP_VERBOSE(edev, NETIF_MSG_IFUP,
+                          "Can't support flow of type %08x\n", fs->flow_type);
+               return -EOPNOTSUPP;
+       }
+
+       input.fs = fs;
+       flow = ethtool_rx_flow_rule_create(&input);
+       if (IS_ERR(flow))
+               return PTR_ERR(flow);
+
+       if (qede_parse_flow_attr(edev, proto, flow->rule, t)) {
+               err = -EINVAL;
+               goto err_out;
+       }
+
+       /* Make sure location is valid and filter isn't already set */
+       err = qede_flow_spec_validate(edev, &flow->rule->action, t,
+                                     fs->location);
+err_out:
+       ethtool_rx_flow_rule_destroy(flow);
+       return err;
+
+}
+
+int qede_add_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info)
+{
+       struct ethtool_rx_flow_spec *fsp = &info->fs;
+       struct qede_arfs_fltr_node *n;
+       struct qede_arfs_tuple t;
+       int min_hlen, rc;
+
+       __qede_lock(edev);
+
+       if (!edev->arfs) {
+               rc = -EPERM;
+               goto unlock;
+       }
+
+       /* Translate the flow specification into something fittign our DB */
+       rc = qede_flow_spec_to_rule(edev, &t, fsp);
+       if (rc)
+               goto unlock;
+
+       if (qede_flow_find_fltr(edev, &t)) {
+               rc = -EINVAL;
+               goto unlock;
+       }
+
+       n = kzalloc(sizeof(*n), GFP_KERNEL);
+       if (!n) {
+               rc = -ENOMEM;
+               goto unlock;
+       }
+
+       min_hlen = qede_flow_get_min_header_size(&t);
+       n->data = kzalloc(min_hlen, GFP_KERNEL);
+       if (!n->data) {
+               kfree(n);
+               rc = -ENOMEM;
+               goto unlock;
+       }
+
+       n->sw_id = fsp->location;
+       set_bit(n->sw_id, edev->arfs->arfs_fltr_bmap);
+       n->buf_len = min_hlen;
+
+       memcpy(&n->tuple, &t, sizeof(n->tuple));
+
+       qede_flow_set_destination(edev, n, fsp);
+
+       /* Build a minimal header according to the flow */
+       n->tuple.build_hdr(&n->tuple, n->data);
+
+       rc = qede_enqueue_fltr_and_config_searcher(edev, n, 0);
+       if (rc)
+               goto unlock;
+
+       qede_configure_arfs_fltr(edev, n, n->rxq_id, true);
+       rc = qede_poll_arfs_filter_config(edev, n);
+unlock:
+       __qede_unlock(edev);
+
+       return rc;
+}
index bdf816f..31b046e 100644 (file)
@@ -1695,6 +1695,19 @@ netdev_tx_t qede_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        return NETDEV_TX_OK;
 }
 
+u16 qede_select_queue(struct net_device *dev, struct sk_buff *skb,
+                     struct net_device *sb_dev,
+                     select_queue_fallback_t fallback)
+{
+       struct qede_dev *edev = netdev_priv(dev);
+       int total_txq;
+
+       total_txq = QEDE_TSS_COUNT(edev) * edev->dev_info.num_tc;
+
+       return QEDE_TSS_COUNT(edev) ?
+               fallback(dev, skb, NULL) % total_txq :  0;
+}
+
 /* 8B udp header + 8B base tunnel header + 32B option length */
 #define QEDE_MAX_TUN_HDR_LEN 48
 
index 6b4d966..02a97c6 100644 (file)
@@ -621,6 +621,7 @@ static const struct net_device_ops qede_netdev_ops = {
        .ndo_open = qede_open,
        .ndo_stop = qede_close,
        .ndo_start_xmit = qede_start_xmit,
+       .ndo_select_queue = qede_select_queue,
        .ndo_set_rx_mode = qede_set_rx_mode,
        .ndo_set_mac_address = qede_set_mac_addr,
        .ndo_validate_addr = eth_validate_addr,
@@ -656,6 +657,7 @@ static const struct net_device_ops qede_netdev_vf_ops = {
        .ndo_open = qede_open,
        .ndo_stop = qede_close,
        .ndo_start_xmit = qede_start_xmit,
+       .ndo_select_queue = qede_select_queue,
        .ndo_set_rx_mode = qede_set_rx_mode,
        .ndo_set_mac_address = qede_set_mac_addr,
        .ndo_validate_addr = eth_validate_addr,
@@ -674,6 +676,7 @@ static const struct net_device_ops qede_netdev_vf_xdp_ops = {
        .ndo_open = qede_open,
        .ndo_stop = qede_close,
        .ndo_start_xmit = qede_start_xmit,
+       .ndo_select_queue = qede_select_queue,
        .ndo_set_rx_mode = qede_set_rx_mode,
        .ndo_set_mac_address = qede_set_mac_addr,
        .ndo_validate_addr = eth_validate_addr,
index 5edbd53..a6886cc 100644 (file)
@@ -249,8 +249,8 @@ static void ql_update_stats(struct ql_adapter *qdev)
 
        spin_lock(&qdev->stats_lock);
        if (ql_sem_spinlock(qdev, qdev->xg_sem_mask)) {
-                       netif_err(qdev, drv, qdev->ndev,
-                                 "Couldn't get xgmac sem.\n");
+               netif_err(qdev, drv, qdev->ndev,
+                         "Couldn't get xgmac sem.\n");
                goto quit;
        }
        /*
index 059ba94..096515c 100644 (file)
@@ -1159,10 +1159,10 @@ static void ql_update_lbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
 
                        map = lbq_desc->p.pg_chunk.map +
                                lbq_desc->p.pg_chunk.offset;
-                               dma_unmap_addr_set(lbq_desc, mapaddr, map);
+                       dma_unmap_addr_set(lbq_desc, mapaddr, map);
                        dma_unmap_len_set(lbq_desc, maplen,
                                        rx_ring->lbq_buf_size);
-                               *lbq_desc->addr = cpu_to_le64(map);
+                       *lbq_desc->addr = cpu_to_le64(map);
 
                        pci_dma_sync_single_for_device(qdev->pdev, map,
                                                rx_ring->lbq_buf_size,
index 8d79031..20d2400 100644 (file)
@@ -1204,7 +1204,7 @@ void emac_mac_tx_process(struct emac_adapter *adpt, struct emac_tx_queue *tx_q)
                if (tpbuf->skb) {
                        pkts_compl++;
                        bytes_compl += tpbuf->skb->len;
-                       dev_kfree_skb_irq(tpbuf->skb);
+                       dev_consume_skb_irq(tpbuf->skb);
                        tpbuf->skb = NULL;
                }
 
index 44f6e48..4f910c4 100644 (file)
@@ -691,7 +691,7 @@ static void cp_tx (struct cp_private *cp)
                        }
                        bytes_compl += skb->len;
                        pkts_compl++;
-                       dev_kfree_skb_irq(skb);
+                       dev_consume_skb_irq(skb);
                }
 
                cp->tx_skb[tx_tail] = NULL;
index 3e650bd..10bab1f 100644 (file)
@@ -1288,11 +1288,13 @@ static u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr)
 static void rtl_ack_events(struct rtl8169_private *tp, u16 bits)
 {
        RTL_W16(tp, IntrStatus, bits);
+       mmiowb();
 }
 
 static void rtl_irq_disable(struct rtl8169_private *tp)
 {
        RTL_W16(tp, IntrMask, 0);
+       mmiowb();
 }
 
 #define RTL_EVENT_NAPI_RX      (RxOK | RxErr)
@@ -1371,41 +1373,6 @@ static void rtl_link_chg_patch(struct rtl8169_private *tp)
 
 #define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)
 
-static u32 __rtl8169_get_wol(struct rtl8169_private *tp)
-{
-       u8 options;
-       u32 wolopts = 0;
-
-       options = RTL_R8(tp, Config1);
-       if (!(options & PMEnable))
-               return 0;
-
-       options = RTL_R8(tp, Config3);
-       if (options & LinkUp)
-               wolopts |= WAKE_PHY;
-       switch (tp->mac_version) {
-       case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38:
-       case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_51:
-               if (rtl_eri_read(tp, 0xdc, ERIAR_EXGMAC) & MagicPacket_v2)
-                       wolopts |= WAKE_MAGIC;
-               break;
-       default:
-               if (options & MagicPacket)
-                       wolopts |= WAKE_MAGIC;
-               break;
-       }
-
-       options = RTL_R8(tp, Config5);
-       if (options & UWF)
-               wolopts |= WAKE_UCAST;
-       if (options & BWF)
-               wolopts |= WAKE_BCAST;
-       if (options & MWF)
-               wolopts |= WAKE_MCAST;
-
-       return wolopts;
-}
-
 static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 {
        struct rtl8169_private *tp = netdev_priv(dev);
@@ -4282,17 +4249,6 @@ static void rtl_wol_suspend_quirk(struct rtl8169_private *tp)
        }
 }
 
-static bool rtl_wol_pll_power_down(struct rtl8169_private *tp)
-{
-       if (!__rtl8169_get_wol(tp))
-               return false;
-
-       phy_speed_down(tp->phydev, false);
-       rtl_wol_suspend_quirk(tp);
-
-       return true;
-}
-
 static void r8168_pll_power_down(struct rtl8169_private *tp)
 {
        if (r8168_check_dash(tp))
@@ -4302,8 +4258,11 @@ static void r8168_pll_power_down(struct rtl8169_private *tp)
            tp->mac_version == RTL_GIGA_MAC_VER_33)
                rtl_ephy_write(tp, 0x19, 0xff64);
 
-       if (rtl_wol_pll_power_down(tp))
+       if (device_may_wakeup(tp_to_dev(tp))) {
+               phy_speed_down(tp->phydev, false);
+               rtl_wol_suspend_quirk(tp);
                return;
+       }
 
        switch (tp->mac_version) {
        case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_33:
@@ -6235,7 +6194,6 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
        struct device *d = tp_to_dev(tp);
        dma_addr_t mapping;
        u32 opts[2], len;
-       bool stop_queue;
        int frags;
 
        if (unlikely(!rtl_tx_slots_avail(tp, skb_shinfo(skb)->nr_frags))) {
@@ -6277,6 +6235,8 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
 
        txd->opts2 = cpu_to_le32(opts[1]);
 
+       netdev_sent_queue(dev, skb->len);
+
        skb_tx_timestamp(skb);
 
        /* Force memory writes to complete before releasing descriptor */
@@ -6289,14 +6249,16 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
 
        tp->cur_tx += frags + 1;
 
-       stop_queue = !rtl_tx_slots_avail(tp, MAX_SKB_FRAGS);
-       if (unlikely(stop_queue))
-               netif_stop_queue(dev);
+       RTL_W8(tp, TxPoll, NPQ);
 
-       if (__netdev_sent_queue(dev, skb->len, skb->xmit_more))
-               RTL_W8(tp, TxPoll, NPQ);
+       mmiowb();
 
-       if (unlikely(stop_queue)) {
+       if (!rtl_tx_slots_avail(tp, MAX_SKB_FRAGS)) {
+               /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must
+                * not miss a ring update when it notices a stopped queue.
+                */
+               smp_wmb();
+               netif_stop_queue(dev);
                /* Sync with rtl_tx:
                 * - publish queue status and cur_tx ring index (write barrier)
                 * - refresh dirty_tx ring index (read barrier).
@@ -6635,7 +6597,9 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
 
        if (work_done < budget) {
                napi_complete_done(napi, work_done);
+
                rtl_irq_enable(tp);
+               mmiowb();
        }
 
        return work_done;
@@ -7153,6 +7117,30 @@ static int rtl_alloc_irq(struct rtl8169_private *tp)
        return pci_alloc_irq_vectors(tp->pci_dev, 1, 1, flags);
 }
 
+static void rtl_read_mac_address(struct rtl8169_private *tp,
+                                u8 mac_addr[ETH_ALEN])
+{
+       u32 value;
+
+       /* Get MAC address */
+       switch (tp->mac_version) {
+       case RTL_GIGA_MAC_VER_35 ... RTL_GIGA_MAC_VER_38:
+       case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_51:
+               value = rtl_eri_read(tp, 0xe0, ERIAR_EXGMAC);
+               mac_addr[0] = (value >>  0) & 0xff;
+               mac_addr[1] = (value >>  8) & 0xff;
+               mac_addr[2] = (value >> 16) & 0xff;
+               mac_addr[3] = (value >> 24) & 0xff;
+
+               value = rtl_eri_read(tp, 0xe4, ERIAR_EXGMAC);
+               mac_addr[4] = (value >>  0) & 0xff;
+               mac_addr[5] = (value >>  8) & 0xff;
+               break;
+       default:
+               break;
+       }
+}
+
 DECLARE_RTL_COND(rtl_link_list_ready_cond)
 {
        return RTL_R8(tp, MCU) & LINK_LIST_RDY;
@@ -7344,6 +7332,8 @@ static int rtl_get_ether_clk(struct rtl8169_private *tp)
 static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        const struct rtl_cfg_info *cfg = rtl_cfg_infos + ent->driver_data;
+       /* align to u16 for is_valid_ether_addr() */
+       u8 mac_addr[ETH_ALEN] __aligned(2) = {};
        struct rtl8169_private *tp;
        struct net_device *dev;
        int chipset, region, i;
@@ -7441,27 +7431,19 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                return rc;
        }
 
-       tp->saved_wolopts = __rtl8169_get_wol(tp);
-
        mutex_init(&tp->wk.mutex);
        INIT_WORK(&tp->wk.work, rtl_task);
        u64_stats_init(&tp->rx_stats.syncp);
        u64_stats_init(&tp->tx_stats.syncp);
 
-       /* Get MAC address */
-       switch (tp->mac_version) {
-               u8 mac_addr[ETH_ALEN] __aligned(4);
-       case RTL_GIGA_MAC_VER_35 ... RTL_GIGA_MAC_VER_38:
-       case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_51:
-               *(u32 *)&mac_addr[0] = rtl_eri_read(tp, 0xe0, ERIAR_EXGMAC);
-               *(u16 *)&mac_addr[4] = rtl_eri_read(tp, 0xe4, ERIAR_EXGMAC);
+       /* get MAC address */
+       rc = eth_platform_get_mac_address(&pdev->dev, mac_addr);
+       if (rc)
+               rtl_read_mac_address(tp, mac_addr);
+
+       if (is_valid_ether_addr(mac_addr))
+               rtl_rar_set(tp, mac_addr);
 
-               if (is_valid_ether_addr(mac_addr))
-                       rtl_rar_set(tp, mac_addr);
-               break;
-       default:
-               break;
-       }
        for (i = 0; i < ETH_ALEN; i++)
                dev->dev_addr[i] = RTL_R8(tp, MAC0 + i);
 
index f27a0dc..339b2ea 100644 (file)
@@ -555,7 +555,7 @@ static int sh_eth_soft_reset_gether(struct net_device *ndev)
        sh_eth_write(ndev, 0, RDFFR);
 
        /* Reset HW CRC register */
-       if (mdp->cd->hw_checksum)
+       if (mdp->cd->csmr)
                sh_eth_write(ndev, 0, CSMR);
 
        /* Select MII mode */
@@ -619,7 +619,8 @@ static struct sh_eth_cpu_data r7s72100_data = {
        .no_trimd       = 1,
        .no_ade         = 1,
        .xdfar_rw       = 1,
-       .hw_checksum    = 1,
+       .csmr           = 1,
+       .rx_csum        = 1,
        .tsu            = 1,
        .no_tx_cntrs    = 1,
 };
@@ -668,7 +669,8 @@ static struct sh_eth_cpu_data r8a7740_data = {
        .no_trimd       = 1,
        .no_ade         = 1,
        .xdfar_rw       = 1,
-       .hw_checksum    = 1,
+       .csmr           = 1,
+       .rx_csum        = 1,
        .tsu            = 1,
        .select_mii     = 1,
        .magic          = 1,
@@ -793,7 +795,8 @@ static struct sh_eth_cpu_data r8a77980_data = {
        .no_trimd       = 1,
        .no_ade         = 1,
        .xdfar_rw       = 1,
-       .hw_checksum    = 1,
+       .csmr           = 1,
+       .rx_csum        = 1,
        .select_mii     = 1,
        .magic          = 1,
        .cexcr          = 1,
@@ -1045,7 +1048,8 @@ static struct sh_eth_cpu_data sh7734_data = {
        .no_ade         = 1,
        .xdfar_rw       = 1,
        .tsu            = 1,
-       .hw_checksum    = 1,
+       .csmr           = 1,
+       .rx_csum        = 1,
        .select_mii     = 1,
        .magic          = 1,
        .cexcr          = 1,
@@ -1088,6 +1092,7 @@ static struct sh_eth_cpu_data sh7763_data = {
        .irq_flags      = IRQF_SHARED,
        .magic          = 1,
        .cexcr          = 1,
+       .rx_csum        = 1,
        .dual_port      = 1,
 };
 
@@ -1532,8 +1537,9 @@ static int sh_eth_dev_init(struct net_device *ndev)
        mdp->irq_enabled = true;
        sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR);
 
-       /* PAUSE Prohibition */
+       /* EMAC Mode: PAUSE prohibition; Duplex; RX Checksum; TX; RX */
        sh_eth_write(ndev, ECMR_ZPF | (mdp->duplex ? ECMR_DM : 0) |
+                    (ndev->features & NETIF_F_RXCSUM ? ECMR_RCSC : 0) |
                     ECMR_TE | ECMR_RE, ECMR);
 
        if (mdp->cd->set_rate)
@@ -1592,6 +1598,19 @@ static void sh_eth_dev_exit(struct net_device *ndev)
        update_mac_address(ndev);
 }
 
+static void sh_eth_rx_csum(struct sk_buff *skb)
+{
+       u8 *hw_csum;
+
+       /* The hardware checksum is 2 bytes appended to packet data */
+       if (unlikely(skb->len < sizeof(__sum16)))
+               return;
+       hw_csum = skb_tail_pointer(skb) - sizeof(__sum16);
+       skb->csum = csum_unfold((__force __sum16)get_unaligned_le16(hw_csum));
+       skb->ip_summed = CHECKSUM_COMPLETE;
+       skb_trim(skb, skb->len - sizeof(__sum16));
+}
+
 /* Packet receive function */
 static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
 {
@@ -1633,7 +1652,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                 * the RFS bits are from bit 25 to bit 16. So, the
                 * driver needs right shifting by 16.
                 */
-               if (mdp->cd->hw_checksum)
+               if (mdp->cd->csmr)
                        desc_status >>= 16;
 
                skb = mdp->rx_skbuff[entry];
@@ -1666,6 +1685,8 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                                         DMA_FROM_DEVICE);
                        skb_put(skb, pkt_len);
                        skb->protocol = eth_type_trans(skb, ndev);
+                       if (ndev->features & NETIF_F_RXCSUM)
+                               sh_eth_rx_csum(skb);
                        netif_receive_skb(skb);
                        ndev->stats.rx_packets++;
                        ndev->stats.rx_bytes += pkt_len;
@@ -2173,7 +2194,7 @@ static size_t __sh_eth_get_regs(struct net_device *ndev, u32 *buf)
        add_reg(MAFCR);
        if (cd->rtrate)
                add_reg(RTRATE);
-       if (cd->hw_checksum)
+       if (cd->csmr)
                add_reg(CSMR);
        if (cd->select_mii)
                add_reg(RMII_MII);
@@ -2921,6 +2942,39 @@ static void sh_eth_set_rx_mode(struct net_device *ndev)
        spin_unlock_irqrestore(&mdp->lock, flags);
 }
 
+static void sh_eth_set_rx_csum(struct net_device *ndev, bool enable)
+{
+       struct sh_eth_private *mdp = netdev_priv(ndev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&mdp->lock, flags);
+
+       /* Disable TX and RX */
+       sh_eth_rcv_snd_disable(ndev);
+
+       /* Modify RX Checksum setting */
+       sh_eth_modify(ndev, ECMR, ECMR_RCSC, enable ? ECMR_RCSC : 0);
+
+       /* Enable TX and RX */
+       sh_eth_rcv_snd_enable(ndev);
+
+       spin_unlock_irqrestore(&mdp->lock, flags);
+}
+
+static int sh_eth_set_features(struct net_device *ndev,
+                              netdev_features_t features)
+{
+       netdev_features_t changed = ndev->features ^ features;
+       struct sh_eth_private *mdp = netdev_priv(ndev);
+
+       if (changed & NETIF_F_RXCSUM && mdp->cd->rx_csum)
+               sh_eth_set_rx_csum(ndev, features & NETIF_F_RXCSUM);
+
+       ndev->features = features;
+
+       return 0;
+}
+
 static int sh_eth_get_vtag_index(struct sh_eth_private *mdp)
 {
        if (!mdp->port)
@@ -3102,6 +3156,7 @@ static const struct net_device_ops sh_eth_netdev_ops = {
        .ndo_change_mtu         = sh_eth_change_mtu,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_set_features       = sh_eth_set_features,
 };
 
 static const struct net_device_ops sh_eth_netdev_ops_tsu = {
@@ -3117,6 +3172,7 @@ static const struct net_device_ops sh_eth_netdev_ops_tsu = {
        .ndo_change_mtu         = sh_eth_change_mtu,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_set_features       = sh_eth_set_features,
 };
 
 #ifdef CONFIG_OF
@@ -3245,6 +3301,11 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
        ndev->max_mtu = 2000 - (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN);
        ndev->min_mtu = ETH_MIN_MTU;
 
+       if (mdp->cd->rx_csum) {
+               ndev->features = NETIF_F_RXCSUM;
+               ndev->hw_features = NETIF_F_RXCSUM;
+       }
+
        /* set function */
        if (mdp->cd->tsu)
                ndev->netdev_ops = &sh_eth_netdev_ops_tsu;
@@ -3294,7 +3355,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
                        goto out_release;
                }
                mdp->port = port;
-               ndev->features = NETIF_F_HW_VLAN_CTAG_FILTER;
+               ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
                /* Need to init only the first port of the two sharing a TSU */
                if (port == 0) {
index 0c18650..8507263 100644 (file)
@@ -499,7 +499,8 @@ struct sh_eth_cpu_data {
        unsigned no_ade:1;      /* E-DMAC DOES NOT have ADE bit in EESR */
        unsigned no_xdfar:1;    /* E-DMAC DOES NOT have RDFAR/TDFAR */
        unsigned xdfar_rw:1;    /* E-DMAC has writeable RDFAR/TDFAR */
-       unsigned hw_checksum:1; /* E-DMAC has CSMR */
+       unsigned csmr:1;        /* E-DMAC has CSMR */
+       unsigned rx_csum:1;     /* EtherC has ECMR.RCSC */
        unsigned select_mii:1;  /* EtherC has RMII_MII (MII select register) */
        unsigned rmiimode:1;    /* EtherC has RMIIMODE register */
        unsigned rtrate:1;      /* EtherC has RTRATE register */
index 748fb12..2b2e1c4 100644 (file)
@@ -109,8 +109,6 @@ struct rocker_world_ops {
        int (*port_attr_bridge_flags_set)(struct rocker_port *rocker_port,
                                          unsigned long brport_flags,
                                          struct switchdev_trans *trans);
-       int (*port_attr_bridge_flags_get)(const struct rocker_port *rocker_port,
-                                         unsigned long *p_brport_flags);
        int (*port_attr_bridge_flags_support_get)(const struct rocker_port *
                                                  rocker_port,
                                                  unsigned long *
index 62a205e..5ce8d5a 100644 (file)
@@ -1583,17 +1583,6 @@ rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
 }
 
 static int
-rocker_world_port_attr_bridge_flags_get(const struct rocker_port *rocker_port,
-                                       unsigned long *p_brport_flags)
-{
-       struct rocker_world_ops *wops = rocker_port->rocker->wops;
-
-       if (!wops->port_attr_bridge_flags_get)
-               return -EOPNOTSUPP;
-       return wops->port_attr_bridge_flags_get(rocker_port, p_brport_flags);
-}
-
-static int
 rocker_world_port_attr_bridge_flags_support_get(const struct rocker_port *
                                                rocker_port,
                                                unsigned long *
@@ -2026,6 +2015,18 @@ static void rocker_port_neigh_destroy(struct net_device *dev,
                            err);
 }
 
+static int rocker_port_get_port_parent_id(struct net_device *dev,
+                                         struct netdev_phys_item_id *ppid)
+{
+       const struct rocker_port *rocker_port = netdev_priv(dev);
+       const struct rocker *rocker = rocker_port->rocker;
+
+       ppid->id_len = sizeof(rocker->hw.id);
+       memcpy(&ppid->id, &rocker->hw.id, ppid->id_len);
+
+       return 0;
+}
+
 static const struct net_device_ops rocker_port_netdev_ops = {
        .ndo_open                       = rocker_port_open,
        .ndo_stop                       = rocker_port_stop,
@@ -2035,6 +2036,7 @@ static const struct net_device_ops rocker_port_netdev_ops = {
        .ndo_get_phys_port_name         = rocker_port_get_phys_port_name,
        .ndo_change_proto_down          = rocker_port_change_proto_down,
        .ndo_neigh_destroy              = rocker_port_neigh_destroy,
+       .ndo_get_port_parent_id         = rocker_port_get_port_parent_id,
 };
 
 /********************
@@ -2045,18 +2047,9 @@ static int rocker_port_attr_get(struct net_device *dev,
                                struct switchdev_attr *attr)
 {
        const struct rocker_port *rocker_port = netdev_priv(dev);
-       const struct rocker *rocker = rocker_port->rocker;
        int err = 0;
 
        switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = sizeof(rocker->hw.id);
-               memcpy(&attr->u.ppid.id, &rocker->hw.id, attr->u.ppid.id_len);
-               break;
-       case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
-               err = rocker_world_port_attr_bridge_flags_get(rocker_port,
-                                                             &attr->u.brport_flags);
-               break;
        case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
                err = rocker_world_port_attr_bridge_flags_support_get(rocker_port,
                                                                      &attr->u.brport_flags_support);
index bea7895..fa296a7 100644 (file)
@@ -2512,16 +2512,6 @@ static int ofdpa_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
 }
 
 static int
-ofdpa_port_attr_bridge_flags_get(const struct rocker_port *rocker_port,
-                                unsigned long *p_brport_flags)
-{
-       const struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
-
-       *p_brport_flags = ofdpa_port->brport_flags;
-       return 0;
-}
-
-static int
 ofdpa_port_attr_bridge_flags_support_get(const struct rocker_port *
                                         rocker_port,
                                         unsigned long *
@@ -2823,7 +2813,6 @@ struct rocker_world_ops rocker_ofdpa_ops = {
        .port_stop = ofdpa_port_stop,
        .port_attr_stp_state_set = ofdpa_port_attr_stp_state_set,
        .port_attr_bridge_flags_set = ofdpa_port_attr_bridge_flags_set,
-       .port_attr_bridge_flags_get = ofdpa_port_attr_bridge_flags_get,
        .port_attr_bridge_flags_support_get = ofdpa_port_attr_bridge_flags_support_get,
        .port_attr_bridge_ageing_time_set = ofdpa_port_attr_bridge_ageing_time_set,
        .port_obj_vlan_add = ofdpa_port_obj_vlan_add,
index 6062e99..e888b47 100644 (file)
@@ -6046,6 +6046,8 @@ static const struct efx_ef10_nvram_type_info efx_ef10_nvram_types[] = {
        { NVRAM_PARTITION_TYPE_DYNCONFIG_DEFAULTS, 0,    0, "sfc_dynamic_cfg_dflt" },
        { NVRAM_PARTITION_TYPE_ROMCONFIG_DEFAULTS, 0,    0, "sfc_exp_rom_cfg_dflt" },
        { NVRAM_PARTITION_TYPE_STATUS,             0,    0, "sfc_status" },
+       { NVRAM_PARTITION_TYPE_BUNDLE,             0,    0, "sfc_bundle" },
+       { NVRAM_PARTITION_TYPE_BUNDLE_METADATA,    0,    0, "sfc_bundle_metadata" },
 };
 #define EF10_NVRAM_PARTITION_COUNT     ARRAY_SIZE(efx_ef10_nvram_types)
 
@@ -6123,7 +6125,7 @@ static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
 static int efx_ef10_mtd_probe(struct efx_nic *efx)
 {
        MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX);
-       DECLARE_BITMAP(found, EF10_NVRAM_PARTITION_COUNT);
+       DECLARE_BITMAP(found, EF10_NVRAM_PARTITION_COUNT) = { 0 };
        struct efx_mcdi_mtd_partition *parts;
        size_t outlen, n_parts_total, i, n_parts;
        unsigned int type;
index 3643015..bc655ff 100644 (file)
@@ -915,7 +915,7 @@ rollback:
 
 void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue)
 {
-       mod_timer(&rx_queue->slow_fill, jiffies + msecs_to_jiffies(100));
+       mod_timer(&rx_queue->slow_fill, jiffies + msecs_to_jiffies(10));
 }
 
 static bool efx_default_channel_want_txqs(struct efx_channel *channel)
index 3839eec..20a5523 100644 (file)
  * subset of the information stored in this partition.
  */
 #define          NVRAM_PARTITION_TYPE_FRU_INFORMATION 0x1d00
+/* enum: Bundle image partition */
+#define          NVRAM_PARTITION_TYPE_BUNDLE 0x1e00
+/* enum: Bundle metadata partition that holds additional information related to
+ * a bundle update in TLV format
+ */
+#define          NVRAM_PARTITION_TYPE_BUNDLE_METADATA 0x1e01
+/* enum: Bundle update non-volatile log output partition */
+#define          NVRAM_PARTITION_TYPE_BUNDLE_LOG 0x1e02
 /* enum: Start of reserved value range (firmware may use for any purpose) */
 #define          NVRAM_PARTITION_TYPE_RESERVED_VALUES_MIN 0xff00
 /* enum: End of reserved value range (firmware may use for any purpose) */
index 396ff01..8702ab4 100644 (file)
@@ -360,8 +360,7 @@ void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue, bool atomic)
                rc = efx_init_rx_buffers(rx_queue, atomic);
                if (unlikely(rc)) {
                        /* Ensure that we don't leave the rx queue empty */
-                       if (rx_queue->added_count == rx_queue->removed_count)
-                               efx_schedule_slow_fill(rx_queue);
+                       efx_schedule_slow_fill(rx_queue);
                        goto out;
                }
        } while ((space -= batch_size) >= batch_size);
index 22eb059..06c8f28 100644 (file)
@@ -471,7 +471,7 @@ static int efx_tx_tso_fallback(struct efx_tx_queue *tx_queue,
        if (IS_ERR(segments))
                return PTR_ERR(segments);
 
-       dev_kfree_skb_any(skb);
+       dev_consume_skb_any(skb);
        skb = segments;
 
        while (skb) {
index 3140999..358e66b 100644 (file)
@@ -666,7 +666,7 @@ static inline void ioc3_tx(struct net_device *dev)
                packets++;
                skb = ip->tx_skbs[o_entry];
                bytes += skb->len;
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
                ip->tx_skbs[o_entry] = NULL;
 
                o_entry = (o_entry + 1) & 127;          /* Next */
index 0e1b7e9..f127140 100644 (file)
@@ -68,6 +68,8 @@ module_param(timeout, int, 0);
  * packets in and out, so there is place for a packet
  */
 struct meth_private {
+       struct platform_device *pdev;
+
        /* in-memory copy of MAC Control register */
        u64 mac_ctrl;
 
@@ -211,8 +213,8 @@ static void meth_check_link(struct net_device *dev)
 static int meth_init_tx_ring(struct meth_private *priv)
 {
        /* Init TX ring */
-       priv->tx_ring = dma_alloc_coherent(NULL, TX_RING_BUFFER_SIZE,
-                                          &priv->tx_ring_dma, GFP_ATOMIC);
+       priv->tx_ring = dma_alloc_coherent(&priv->pdev->dev,
+                       TX_RING_BUFFER_SIZE, &priv->tx_ring_dma, GFP_ATOMIC);
        if (!priv->tx_ring)
                return -ENOMEM;
 
@@ -236,7 +238,7 @@ static int meth_init_rx_ring(struct meth_private *priv)
                priv->rx_ring[i]=(rx_packet*)(priv->rx_skbs[i]->head);
                /* I'll need to re-sync it after each RX */
                priv->rx_ring_dmas[i] =
-                       dma_map_single(NULL, priv->rx_ring[i],
+                       dma_map_single(&priv->pdev->dev, priv->rx_ring[i],
                                       METH_RX_BUFF_SIZE, DMA_FROM_DEVICE);
                mace->eth.rx_fifo = priv->rx_ring_dmas[i];
        }
@@ -253,7 +255,7 @@ static void meth_free_tx_ring(struct meth_private *priv)
                        dev_kfree_skb(priv->tx_skbs[i]);
                priv->tx_skbs[i] = NULL;
        }
-       dma_free_coherent(NULL, TX_RING_BUFFER_SIZE, priv->tx_ring,
+       dma_free_coherent(&priv->pdev->dev, TX_RING_BUFFER_SIZE, priv->tx_ring,
                          priv->tx_ring_dma);
 }
 
@@ -263,7 +265,7 @@ static void meth_free_rx_ring(struct meth_private *priv)
        int i;
 
        for (i = 0; i < RX_RING_ENTRIES; i++) {
-               dma_unmap_single(NULL, priv->rx_ring_dmas[i],
+               dma_unmap_single(&priv->pdev->dev, priv->rx_ring_dmas[i],
                                 METH_RX_BUFF_SIZE, DMA_FROM_DEVICE);
                priv->rx_ring[i] = 0;
                priv->rx_ring_dmas[i] = 0;
@@ -393,7 +395,8 @@ static void meth_rx(struct net_device* dev, unsigned long int_status)
                fifo_rptr = (fifo_rptr - 1) & 0x0f;
        }
        while (priv->rx_write != fifo_rptr) {
-               dma_unmap_single(NULL, priv->rx_ring_dmas[priv->rx_write],
+               dma_unmap_single(&priv->pdev->dev,
+                                priv->rx_ring_dmas[priv->rx_write],
                                 METH_RX_BUFF_SIZE, DMA_FROM_DEVICE);
                status = priv->rx_ring[priv->rx_write]->status.raw;
 #if MFE_DEBUG
@@ -454,7 +457,8 @@ static void meth_rx(struct net_device* dev, unsigned long int_status)
                priv->rx_ring[priv->rx_write] = (rx_packet*)skb->head;
                priv->rx_ring[priv->rx_write]->status.raw = 0;
                priv->rx_ring_dmas[priv->rx_write] =
-                       dma_map_single(NULL, priv->rx_ring[priv->rx_write],
+                       dma_map_single(&priv->pdev->dev,
+                                      priv->rx_ring[priv->rx_write],
                                       METH_RX_BUFF_SIZE, DMA_FROM_DEVICE);
                mace->eth.rx_fifo = priv->rx_ring_dmas[priv->rx_write];
                ADVANCE_RX_PTR(priv->rx_write);
@@ -521,7 +525,7 @@ static void meth_tx_cleanup(struct net_device* dev, unsigned long int_status)
                        DPRINTK("RPTR points us here, but packet not done?\n");
                        break;
                }
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
                priv->tx_skbs[priv->tx_read] = NULL;
                priv->tx_ring[priv->tx_read].header.raw = 0;
                priv->tx_read = (priv->tx_read+1)&(TX_RING_ENTRIES-1);
@@ -637,7 +641,7 @@ static void meth_tx_1page_prepare(struct meth_private *priv,
        }
 
        /* first page */
-       catbuf = dma_map_single(NULL, buffer_data, buffer_len,
+       catbuf = dma_map_single(&priv->pdev->dev, buffer_data, buffer_len,
                                DMA_TO_DEVICE);
        desc->data.cat_buf[0].form.start_addr = catbuf >> 3;
        desc->data.cat_buf[0].form.len = buffer_len - 1;
@@ -663,12 +667,12 @@ static void meth_tx_2page_prepare(struct meth_private *priv,
        }
 
        /* first page */
-       catbuf1 = dma_map_single(NULL, buffer1_data, buffer1_len,
+       catbuf1 = dma_map_single(&priv->pdev->dev, buffer1_data, buffer1_len,
                                 DMA_TO_DEVICE);
        desc->data.cat_buf[0].form.start_addr = catbuf1 >> 3;
        desc->data.cat_buf[0].form.len = buffer1_len - 1;
        /* second page */
-       catbuf2 = dma_map_single(NULL, buffer2_data, buffer2_len,
+       catbuf2 = dma_map_single(&priv->pdev->dev, buffer2_data, buffer2_len,
                                 DMA_TO_DEVICE);
        desc->data.cat_buf[1].form.start_addr = catbuf2 >> 3;
        desc->data.cat_buf[1].form.len = buffer2_len - 1;
@@ -840,6 +844,7 @@ static int meth_probe(struct platform_device *pdev)
        memcpy(dev->dev_addr, o2meth_eaddr, ETH_ALEN);
 
        priv = netdev_priv(dev);
+       priv->pdev = pdev;
        spin_lock_init(&priv->meth_lock);
        SET_NETDEV_DEV(dev, &pdev->dev);
 
index 808cf98..5b351be 100644 (file)
@@ -714,7 +714,7 @@ static void sis190_tx_interrupt(struct net_device *dev,
 
                sis190_unmap_tx_skb(tp->pci_dev, skb, txd);
                tp->Tx_skbuff[entry] = NULL;
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
        }
 
        if (tp->dirty_tx != dirty_tx) {
index 4bb89f7..6073387 100644 (file)
@@ -1927,7 +1927,7 @@ static void sis900_finish_xmit (struct net_device *net_dev)
                pci_unmap_single(sis_priv->pci_dev,
                        sis_priv->tx_ring[entry].bufptr, skb->len,
                        PCI_DMA_TODEVICE);
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
                sis_priv->tx_skbuff[entry] = NULL;
                sis_priv->tx_ring[entry].bufptr = 0;
                sis_priv->tx_ring[entry].cmdsts = 0;
index 15c62c1..be47d86 100644 (file)
@@ -1037,7 +1037,7 @@ static void epic_tx(struct net_device *dev, struct epic_private *ep)
                skb = ep->tx_skbuff[entry];
                pci_unmap_single(ep->pci_dev, ep->tx_ring[entry].bufaddr,
                                 skb->len, PCI_DMA_TODEVICE);
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
                ep->tx_skbuff[entry] = NULL;
        }
 
index 8355dfb..b550e62 100644 (file)
@@ -1188,7 +1188,7 @@ smc911x_tx_dma_irq(void *data)
 
        DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, "TX DMA irq handler\n");
        BUG_ON(skb == NULL);
-       dma_unmap_single(NULL, tx_dmabuf, tx_dmalen, DMA_TO_DEVICE);
+       dma_unmap_single(lp->dev, tx_dmabuf, tx_dmalen, DMA_TO_DEVICE);
        netif_trans_update(dev);
        dev_kfree_skb_irq(skb);
        lp->current_tx_skb = NULL;
@@ -1219,7 +1219,7 @@ smc911x_rx_dma_irq(void *data)
 
        DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__);
        DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, dev, "RX DMA irq handler\n");
-       dma_unmap_single(NULL, rx_dmabuf, rx_dmalen, DMA_FROM_DEVICE);
+       dma_unmap_single(lp->dev, rx_dmabuf, rx_dmalen, DMA_FROM_DEVICE);
        BUG_ON(skb == NULL);
        lp->current_rx_skb = NULL;
        PRINT_PKT(skb->data, skb->len);
index 7b92336..3b174ea 100644 (file)
@@ -1342,8 +1342,10 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)
        }
 
        ret = phy_power_on(bsp_priv, true);
-       if (ret)
+       if (ret) {
+               gmac_clk_enable(bsp_priv, false);
                return ret;
+       }
 
        pm_runtime_enable(dev);
        pm_runtime_get_sync(dev);
index 20299f6..90045ff 100644 (file)
@@ -268,7 +268,7 @@ static int dwmac4_wrback_get_rx_timestamp_status(void *desc, void *next_desc,
        int ret = -EINVAL;
 
        /* Get the status from normal w/b descriptor */
-       if (likely(p->des3 & TDES3_RS1V)) {
+       if (likely(le32_to_cpu(p->des3) & RDES3_RDES1_VALID)) {
                if (likely(le32_to_cpu(p->des1) & RDES1_TIMESTAMP_AVAILABLE)) {
                        int i = 0;
 
index d1f61c2..5d85742 100644 (file)
@@ -721,8 +721,11 @@ static u32 stmmac_usec2riwt(u32 usec, struct stmmac_priv *priv)
 {
        unsigned long clk = clk_get_rate(priv->plat->stmmac_clk);
 
-       if (!clk)
-               return 0;
+       if (!clk) {
+               clk = priv->plat->clk_ref_rate;
+               if (!clk)
+                       return 0;
+       }
 
        return (usec * (clk / 1000000)) / 256;
 }
@@ -731,8 +734,11 @@ static u32 stmmac_riwt2usec(u32 riwt, struct stmmac_priv *priv)
 {
        unsigned long clk = clk_get_rate(priv->plat->stmmac_clk);
 
-       if (!clk)
-               return 0;
+       if (!clk) {
+               clk = priv->plat->clk_ref_rate;
+               if (!clk)
+                       return 0;
+       }
 
        return (riwt * 256) / (clk / 1000000);
 }
index 517555b..f12dd59 100644 (file)
@@ -597,12 +597,13 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
                case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
                        /* PTP v1, UDP, any kind of event packet */
                        config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
-                       /* take time stamp for all event messages */
-                       if (xmac)
-                               snap_type_sel = PTP_GMAC4_TCR_SNAPTYPSEL_1;
-                       else
-                               snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
-
+                       /* 'xmac' hardware can support Sync, Pdelay_Req and
+                        * Pdelay_resp by setting bit14 and bits17/16 to 01
+                        * This leaves Delay_Req timestamps out.
+                        * Enable all events *and* general purpose message
+                        * timestamping
+                        */
+                       snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
                        ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
                        ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
                        break;
@@ -633,10 +634,7 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
                        config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
                        ptp_v2 = PTP_TCR_TSVER2ENA;
                        /* take time stamp for all event messages */
-                       if (xmac)
-                               snap_type_sel = PTP_GMAC4_TCR_SNAPTYPSEL_1;
-                       else
-                               snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
+                       snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
 
                        ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
                        ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
@@ -669,12 +667,7 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
                        /* PTP v2/802.AS1 any layer, any kind of event packet */
                        config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
                        ptp_v2 = PTP_TCR_TSVER2ENA;
-                       /* take time stamp for all event messages */
-                       if (xmac)
-                               snap_type_sel = PTP_GMAC4_TCR_SNAPTYPSEL_1;
-                       else
-                               snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
-
+                       snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
                        ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
                        ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
                        ptp_over_ethernet = PTP_TCR_TSIPENA;
@@ -3046,10 +3039,22 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 
        tx_q = &priv->tx_queue[queue];
 
+       if (priv->tx_path_in_lpi_mode)
+               stmmac_disable_eee_mode(priv);
+
        /* Manage oversized TCP frames for GMAC4 device */
        if (skb_is_gso(skb) && priv->tso) {
-               if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
+               if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) {
+                       /*
+                        * There is no way to determine the number of TSO
+                        * capable Queues. Let's use always the Queue 0
+                        * because if TSO is supported then at least this
+                        * one will be capable.
+                        */
+                       skb_set_queue_mapping(skb, 0);
+
                        return stmmac_tso_xmit(skb, dev);
+               }
        }
 
        if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) {
@@ -3064,9 +3069,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
                return NETDEV_TX_BUSY;
        }
 
-       if (priv->tx_path_in_lpi_mode)
-               stmmac_disable_eee_mode(priv);
-
        entry = tx_q->cur_tx;
        first_entry = entry;
        WARN_ON(tx_q->tx_skbuff[first_entry]);
index ecccf89..e852821 100644 (file)
 #define        PTP_TCR_TSEVNTENA       BIT(14)
 /* Enable Snapshot for Messages Relevant to Master */
 #define        PTP_TCR_TSMSTRENA       BIT(15)
-/* Select PTP packets for Taking Snapshots */
+/* Select PTP packets for Taking Snapshots
+ * On gmac4 specifically:
+ * Enable SYNC, Pdelay_Req, Pdelay_Resp when TSEVNTENA is enabled.
+ * or
+ * Enable  SYNC, Follow_Up, Delay_Req, Delay_Resp, Pdelay_Req, Pdelay_Resp,
+ * Pdelay_Resp_Follow_Up if TSEVNTENA is disabled
+ */
 #define        PTP_TCR_SNAPTYPSEL_1    BIT(16)
-#define        PTP_GMAC4_TCR_SNAPTYPSEL_1      GENMASK(17, 16)
 /* Enable MAC address for PTP Frame Filtering */
 #define        PTP_TCR_TSENMACADDR     BIT(18)
 
index 7ec4eb7..6fc05c1 100644 (file)
@@ -1898,7 +1898,7 @@ static inline void cas_tx_ringN(struct cas *cp, int ring, int limit)
                cp->net_stats[ring].tx_packets++;
                cp->net_stats[ring].tx_bytes += skb->len;
                spin_unlock(&cp->stat_lock[ring]);
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
        }
        cp->tx_old[ring] = entry;
 
index 720b7ac..e9b757b 100644 (file)
@@ -781,7 +781,7 @@ static void bigmac_tx(struct bigmac *bp)
 
                DTX(("skb(%p) ", skb));
                bp->tx_skbs[elem] = NULL;
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
 
                elem = NEXT_TX(elem);
        }
index ff641cf..d007dfe 100644 (file)
@@ -1962,7 +1962,7 @@ static void happy_meal_tx(struct happy_meal *hp)
                        this = &txbase[elem];
                }
 
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
                dev->stats.tx_packets++;
        }
        hp->tx_old = elem;
index dc966dd..b24c111 100644 (file)
@@ -1739,7 +1739,7 @@ static void bdx_tx_cleanup(struct bdx_priv *priv)
                tx_level -= db->rptr->len;      /* '-' koz len is negative */
 
                /* now should come skb pointer - free it */
-               dev_kfree_skb_irq(db->rptr->addr.skb);
+               dev_consume_skb_irq(db->rptr->addr.skb);
                bdx_tx_db_inc_rptr(db);
        }
 
index 810dfc7..e2d47b2 100644 (file)
@@ -608,7 +608,7 @@ static void cpmac_end_xmit(struct net_device *dev, int queue)
                        netdev_dbg(dev, "sent 0x%p, len=%d\n",
                                   desc->skb, desc->skb->len);
 
-               dev_kfree_skb_irq(desc->skb);
+               dev_consume_skb_irq(desc->skb);
                desc->skb = NULL;
                if (__netif_subqueue_stopped(dev, queue))
                        netif_wake_subqueue(dev, queue);
index 8241269..27f6cf1 100644 (file)
@@ -1740,7 +1740,7 @@ static void velocity_free_tx_buf(struct velocity_info *vptr,
                dma_unmap_single(vptr->dev, tdinfo->skb_dma[i],
                                 le16_to_cpu(pktlen), DMA_TO_DEVICE);
        }
-       dev_kfree_skb_irq(skb);
+       dev_consume_skb_irq(skb);
        tdinfo->skb = NULL;
 }
 
index 15bb058..44efffb 100644 (file)
@@ -630,7 +630,7 @@ static void temac_start_xmit_done(struct net_device *ndev)
                dma_unmap_single(ndev->dev.parent, cur_p->phys, cur_p->len,
                                 DMA_TO_DEVICE);
                if (cur_p->app4)
-                       dev_kfree_skb_irq((struct sk_buff *)cur_p->app4);
+                       dev_consume_skb_irq((struct sk_buff *)cur_p->app4);
                cur_p->app0 = 0;
                cur_p->app1 = 0;
                cur_p->app2 = 0;
index 0789d8a..ec7e7ec 100644 (file)
@@ -595,7 +595,7 @@ static void axienet_start_xmit_done(struct net_device *ndev)
                                (cur_p->cntrl & XAXIDMA_BD_CTRL_LENGTH_MASK),
                                DMA_TO_DEVICE);
                if (cur_p->app4)
-                       dev_kfree_skb_irq((struct sk_buff *)cur_p->app4);
+                       dev_consume_skb_irq((struct sk_buff *)cur_p->app4);
                /*cur_p->phys = 0;*/
                cur_p->app0 = 0;
                cur_p->app1 = 0;
index 639e3e9..b03a417 100644 (file)
@@ -581,7 +581,7 @@ static void xemaclite_tx_handler(struct net_device *dev)
                return;
 
        dev->stats.tx_bytes += lp->deferred_skb->len;
-       dev_kfree_skb_irq(lp->deferred_skb);
+       dev_consume_skb_irq(lp->deferred_skb);
        lp->deferred_skb = NULL;
        netif_trans_update(dev); /* prevent tx timeout */
        netif_wake_queue(dev);
index aee55c0..ed6623a 100644 (file)
 #ifdef __ARMEB__
 typedef struct sk_buff buffer_t;
 #define free_buffer dev_kfree_skb
-#define free_buffer_irq dev_kfree_skb_irq
+#define free_buffer_irq dev_consume_skb_irq
 #else
 typedef void buffer_t;
 #define free_buffer kfree
index 38ac8ef..56b7791 100644 (file)
@@ -3512,7 +3512,7 @@ static int dfx_xmt_done(DFX_board_t *bp)
                                 bp->descr_block_virt->xmt_data[comp].long_1,
                                 p_xmt_drv_descr->p_skb->len,
                                 DMA_TO_DEVICE);
-               dev_kfree_skb_irq(p_xmt_drv_descr->p_skb);
+               dev_consume_skb_irq(p_xmt_drv_descr->p_skb);
 
                /*
                 * Move to start of next packet by updating completion index
index 6ef44c4..f29f5a6 100644 (file)
@@ -851,6 +851,7 @@ static void pcm_fsm(struct s_smc *smc, struct s_phy *phy, int cmd)
 
        case ACTIONS(PC5_SIGNAL) :
                ACTIONS_DONE() ;
+               /* fall through */
        case PC5_SIGNAL :
                if ((cmd != PC_SIGNAL) && (cmd != PC_TIMEOUT_LCT))
                        break ;
index 58bbba8..3377ac6 100644 (file)
@@ -1512,9 +1512,13 @@ static void geneve_link_config(struct net_device *dev,
        }
 #if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6: {
-               struct rt6_info *rt = rt6_lookup(geneve->net,
-                                                &info->key.u.ipv6.dst, NULL, 0,
-                                                NULL, 0);
+               struct rt6_info *rt;
+
+               if (!__in6_dev_get(dev))
+                       break;
+
+               rt = rt6_lookup(geneve->net, &info->key.u.ipv6.dst, NULL, 0,
+                               NULL, 0);
 
                if (rt && rt->dst.dev)
                        ldev_mtu = rt->dst.dev->mtu - GENEVE_IPV6_HLEN;
index 44de81e..c589f5a 100644 (file)
@@ -905,9 +905,9 @@ mcr20a_irq_clean_complete(void *context)
                }
                break;
        case (DAR_IRQSTS1_RXIRQ | DAR_IRQSTS1_SEQIRQ):
-                       /* rx is starting */
-                       dev_dbg(printdev(lp), "RX is starting\n");
-                       mcr20a_handle_rx(lp);
+               /* rx is starting */
+               dev_dbg(printdev(lp), "RX is starting\n");
+               mcr20a_handle_rx(lp);
                break;
        case (DAR_IRQSTS1_RXIRQ | DAR_IRQSTS1_TXIRQ | DAR_IRQSTS1_SEQIRQ):
                if (lp->is_tx) {
index 8a2c64d..3ee9536 100644 (file)
@@ -5,4 +5,5 @@
 obj-$(CONFIG_IPVLAN) += ipvlan.o
 obj-$(CONFIG_IPVTAP) += ipvtap.o
 
-ipvlan-objs := ipvlan_core.o ipvlan_main.o
+ipvlan-objs-$(CONFIG_IPVLAN_L3S) += ipvlan_l3s.o
+ipvlan-objs := ipvlan_core.o ipvlan_main.o $(ipvlan-objs-y)
index adb826f..b906d2f 100644 (file)
@@ -165,10 +165,9 @@ struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
                                   const void *iaddr, bool is_v6);
 bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6);
 void ipvlan_ht_addr_del(struct ipvl_addr *addr);
-struct sk_buff *ipvlan_l3_rcv(struct net_device *dev, struct sk_buff *skb,
-                             u16 proto);
-unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb,
-                            const struct nf_hook_state *state);
+struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port, void *lyr3h,
+                                    int addr_type, bool use_dest);
+void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type);
 void ipvlan_count_rx(const struct ipvl_dev *ipvlan,
                     unsigned int len, bool success, bool mcast);
 int ipvlan_link_new(struct net *src_net, struct net_device *dev,
@@ -177,6 +176,36 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev,
 void ipvlan_link_delete(struct net_device *dev, struct list_head *head);
 void ipvlan_link_setup(struct net_device *dev);
 int ipvlan_link_register(struct rtnl_link_ops *ops);
+#ifdef CONFIG_IPVLAN_L3S
+int ipvlan_l3s_register(struct ipvl_port *port);
+void ipvlan_l3s_unregister(struct ipvl_port *port);
+void ipvlan_migrate_l3s_hook(struct net *oldnet, struct net *newnet);
+int ipvlan_l3s_init(void);
+void ipvlan_l3s_cleanup(void);
+#else
+static inline int ipvlan_l3s_register(struct ipvl_port *port)
+{
+       return -ENOTSUPP;
+}
+
+static inline void ipvlan_l3s_unregister(struct ipvl_port *port)
+{
+}
+
+static inline void ipvlan_migrate_l3s_hook(struct net *oldnet,
+                                          struct net *newnet)
+{
+}
+
+static inline int ipvlan_l3s_init(void)
+{
+       return 0;
+}
+
+static inline void ipvlan_l3s_cleanup(void)
+{
+}
+#endif /* CONFIG_IPVLAN_L3S */
 
 static inline bool netif_is_ipvlan_port(const struct net_device *dev)
 {
index 1a8132e..e0f5bc8 100644 (file)
@@ -138,7 +138,7 @@ bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6)
        return ret;
 }
 
-static void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type)
+void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type)
 {
        void *lyr3h = NULL;
 
@@ -355,9 +355,8 @@ out:
        return ret;
 }
 
-static struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port,
-                                           void *lyr3h, int addr_type,
-                                           bool use_dest)
+struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port, void *lyr3h,
+                                    int addr_type, bool use_dest)
 {
        struct ipvl_addr *addr = NULL;
 
@@ -647,7 +646,9 @@ int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
        case IPVLAN_MODE_L2:
                return ipvlan_xmit_mode_l2(skb, dev);
        case IPVLAN_MODE_L3:
+#ifdef CONFIG_IPVLAN_L3S
        case IPVLAN_MODE_L3S:
+#endif
                return ipvlan_xmit_mode_l3(skb, dev);
        }
 
@@ -743,8 +744,10 @@ rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb)
                return ipvlan_handle_mode_l2(pskb, port);
        case IPVLAN_MODE_L3:
                return ipvlan_handle_mode_l3(pskb, port);
+#ifdef CONFIG_IPVLAN_L3S
        case IPVLAN_MODE_L3S:
                return RX_HANDLER_PASS;
+#endif
        }
 
        /* Should not reach here */
@@ -753,97 +756,3 @@ rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb)
        kfree_skb(skb);
        return RX_HANDLER_CONSUMED;
 }
-
-static struct ipvl_addr *ipvlan_skb_to_addr(struct sk_buff *skb,
-                                           struct net_device *dev)
-{
-       struct ipvl_addr *addr = NULL;
-       struct ipvl_port *port;
-       void *lyr3h;
-       int addr_type;
-
-       if (!dev || !netif_is_ipvlan_port(dev))
-               goto out;
-
-       port = ipvlan_port_get_rcu(dev);
-       if (!port || port->mode != IPVLAN_MODE_L3S)
-               goto out;
-
-       lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
-       if (!lyr3h)
-               goto out;
-
-       addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
-out:
-       return addr;
-}
-
-struct sk_buff *ipvlan_l3_rcv(struct net_device *dev, struct sk_buff *skb,
-                             u16 proto)
-{
-       struct ipvl_addr *addr;
-       struct net_device *sdev;
-
-       addr = ipvlan_skb_to_addr(skb, dev);
-       if (!addr)
-               goto out;
-
-       sdev = addr->master->dev;
-       switch (proto) {
-       case AF_INET:
-       {
-               int err;
-               struct iphdr *ip4h = ip_hdr(skb);
-
-               err = ip_route_input_noref(skb, ip4h->daddr, ip4h->saddr,
-                                          ip4h->tos, sdev);
-               if (unlikely(err))
-                       goto out;
-               break;
-       }
-#if IS_ENABLED(CONFIG_IPV6)
-       case AF_INET6:
-       {
-               struct dst_entry *dst;
-               struct ipv6hdr *ip6h = ipv6_hdr(skb);
-               int flags = RT6_LOOKUP_F_HAS_SADDR;
-               struct flowi6 fl6 = {
-                       .flowi6_iif   = sdev->ifindex,
-                       .daddr        = ip6h->daddr,
-                       .saddr        = ip6h->saddr,
-                       .flowlabel    = ip6_flowinfo(ip6h),
-                       .flowi6_mark  = skb->mark,
-                       .flowi6_proto = ip6h->nexthdr,
-               };
-
-               skb_dst_drop(skb);
-               dst = ip6_route_input_lookup(dev_net(sdev), sdev, &fl6,
-                                            skb, flags);
-               skb_dst_set(skb, dst);
-               break;
-       }
-#endif
-       default:
-               break;
-       }
-
-out:
-       return skb;
-}
-
-unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb,
-                            const struct nf_hook_state *state)
-{
-       struct ipvl_addr *addr;
-       unsigned int len;
-
-       addr = ipvlan_skb_to_addr(skb, skb->dev);
-       if (!addr)
-               goto out;
-
-       skb->dev = addr->master->dev;
-       len = skb->len + ETH_HLEN;
-       ipvlan_count_rx(addr->master, len, true, false);
-out:
-       return NF_ACCEPT;
-}
diff --git a/drivers/net/ipvlan/ipvlan_l3s.c b/drivers/net/ipvlan/ipvlan_l3s.c
new file mode 100644 (file)
index 0000000..d17480a
--- /dev/null
@@ -0,0 +1,227 @@
+/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
+ *
+ * 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 the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include "ipvlan.h"
+
+static unsigned int ipvlan_netid __read_mostly;
+
+struct ipvlan_netns {
+       unsigned int ipvl_nf_hook_refcnt;
+};
+
+static struct ipvl_addr *ipvlan_skb_to_addr(struct sk_buff *skb,
+                                           struct net_device *dev)
+{
+       struct ipvl_addr *addr = NULL;
+       struct ipvl_port *port;
+       int addr_type;
+       void *lyr3h;
+
+       if (!dev || !netif_is_ipvlan_port(dev))
+               goto out;
+
+       port = ipvlan_port_get_rcu(dev);
+       if (!port || port->mode != IPVLAN_MODE_L3S)
+               goto out;
+
+       lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
+       if (!lyr3h)
+               goto out;
+
+       addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true);
+out:
+       return addr;
+}
+
+static struct sk_buff *ipvlan_l3_rcv(struct net_device *dev,
+                                    struct sk_buff *skb, u16 proto)
+{
+       struct ipvl_addr *addr;
+       struct net_device *sdev;
+
+       addr = ipvlan_skb_to_addr(skb, dev);
+       if (!addr)
+               goto out;
+
+       sdev = addr->master->dev;
+       switch (proto) {
+       case AF_INET:
+       {
+               struct iphdr *ip4h = ip_hdr(skb);
+               int err;
+
+               err = ip_route_input_noref(skb, ip4h->daddr, ip4h->saddr,
+                                          ip4h->tos, sdev);
+               if (unlikely(err))
+                       goto out;
+               break;
+       }
+#if IS_ENABLED(CONFIG_IPV6)
+       case AF_INET6:
+       {
+               struct dst_entry *dst;
+               struct ipv6hdr *ip6h = ipv6_hdr(skb);
+               int flags = RT6_LOOKUP_F_HAS_SADDR;
+               struct flowi6 fl6 = {
+                       .flowi6_iif   = sdev->ifindex,
+                       .daddr        = ip6h->daddr,
+                       .saddr        = ip6h->saddr,
+                       .flowlabel    = ip6_flowinfo(ip6h),
+                       .flowi6_mark  = skb->mark,
+                       .flowi6_proto = ip6h->nexthdr,
+               };
+
+               skb_dst_drop(skb);
+               dst = ip6_route_input_lookup(dev_net(sdev), sdev, &fl6,
+                                            skb, flags);
+               skb_dst_set(skb, dst);
+               break;
+       }
+#endif
+       default:
+               break;
+       }
+out:
+       return skb;
+}
+
+static const struct l3mdev_ops ipvl_l3mdev_ops = {
+       .l3mdev_l3_rcv = ipvlan_l3_rcv,
+};
+
+static unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb,
+                                   const struct nf_hook_state *state)
+{
+       struct ipvl_addr *addr;
+       unsigned int len;
+
+       addr = ipvlan_skb_to_addr(skb, skb->dev);
+       if (!addr)
+               goto out;
+
+       skb->dev = addr->master->dev;
+       len = skb->len + ETH_HLEN;
+       ipvlan_count_rx(addr->master, len, true, false);
+out:
+       return NF_ACCEPT;
+}
+
+static const struct nf_hook_ops ipvl_nfops[] = {
+       {
+               .hook     = ipvlan_nf_input,
+               .pf       = NFPROTO_IPV4,
+               .hooknum  = NF_INET_LOCAL_IN,
+               .priority = INT_MAX,
+       },
+#if IS_ENABLED(CONFIG_IPV6)
+       {
+               .hook     = ipvlan_nf_input,
+               .pf       = NFPROTO_IPV6,
+               .hooknum  = NF_INET_LOCAL_IN,
+               .priority = INT_MAX,
+       },
+#endif
+};
+
+static int ipvlan_register_nf_hook(struct net *net)
+{
+       struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
+       int err = 0;
+
+       if (!vnet->ipvl_nf_hook_refcnt) {
+               err = nf_register_net_hooks(net, ipvl_nfops,
+                                           ARRAY_SIZE(ipvl_nfops));
+               if (!err)
+                       vnet->ipvl_nf_hook_refcnt = 1;
+       } else {
+               vnet->ipvl_nf_hook_refcnt++;
+       }
+
+       return err;
+}
+
+static void ipvlan_unregister_nf_hook(struct net *net)
+{
+       struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
+
+       if (WARN_ON(!vnet->ipvl_nf_hook_refcnt))
+               return;
+
+       vnet->ipvl_nf_hook_refcnt--;
+       if (!vnet->ipvl_nf_hook_refcnt)
+               nf_unregister_net_hooks(net, ipvl_nfops,
+                                       ARRAY_SIZE(ipvl_nfops));
+}
+
+void ipvlan_migrate_l3s_hook(struct net *oldnet, struct net *newnet)
+{
+       struct ipvlan_netns *old_vnet;
+
+       ASSERT_RTNL();
+
+       old_vnet = net_generic(oldnet, ipvlan_netid);
+       if (!old_vnet->ipvl_nf_hook_refcnt)
+               return;
+
+       ipvlan_register_nf_hook(newnet);
+       ipvlan_unregister_nf_hook(oldnet);
+}
+
+static void ipvlan_ns_exit(struct net *net)
+{
+       struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
+
+       if (WARN_ON_ONCE(vnet->ipvl_nf_hook_refcnt)) {
+               vnet->ipvl_nf_hook_refcnt = 0;
+               nf_unregister_net_hooks(net, ipvl_nfops,
+                                       ARRAY_SIZE(ipvl_nfops));
+       }
+}
+
+static struct pernet_operations ipvlan_net_ops = {
+       .id   = &ipvlan_netid,
+       .size = sizeof(struct ipvlan_netns),
+       .exit = ipvlan_ns_exit,
+};
+
+int ipvlan_l3s_init(void)
+{
+       return register_pernet_subsys(&ipvlan_net_ops);
+}
+
+void ipvlan_l3s_cleanup(void)
+{
+       unregister_pernet_subsys(&ipvlan_net_ops);
+}
+
+int ipvlan_l3s_register(struct ipvl_port *port)
+{
+       struct net_device *dev = port->dev;
+       int ret;
+
+       ASSERT_RTNL();
+
+       ret = ipvlan_register_nf_hook(read_pnet(&port->pnet));
+       if (!ret) {
+               dev->l3mdev_ops = &ipvl_l3mdev_ops;
+               dev->priv_flags |= IFF_L3MDEV_RX_HANDLER;
+       }
+
+       return ret;
+}
+
+void ipvlan_l3s_unregister(struct ipvl_port *port)
+{
+       struct net_device *dev = port->dev;
+
+       ASSERT_RTNL();
+
+       dev->priv_flags &= ~IFF_L3MDEV_RX_HANDLER;
+       ipvlan_unregister_nf_hook(read_pnet(&port->pnet));
+       dev->l3mdev_ops = NULL;
+}
index 19bdde6..8ec73d9 100644 (file)
@@ -9,73 +9,10 @@
 
 #include "ipvlan.h"
 
-static unsigned int ipvlan_netid __read_mostly;
-
-struct ipvlan_netns {
-       unsigned int ipvl_nf_hook_refcnt;
-};
-
-static const struct nf_hook_ops ipvl_nfops[] = {
-       {
-               .hook     = ipvlan_nf_input,
-               .pf       = NFPROTO_IPV4,
-               .hooknum  = NF_INET_LOCAL_IN,
-               .priority = INT_MAX,
-       },
-#if IS_ENABLED(CONFIG_IPV6)
-       {
-               .hook     = ipvlan_nf_input,
-               .pf       = NFPROTO_IPV6,
-               .hooknum  = NF_INET_LOCAL_IN,
-               .priority = INT_MAX,
-       },
-#endif
-};
-
-static const struct l3mdev_ops ipvl_l3mdev_ops = {
-       .l3mdev_l3_rcv = ipvlan_l3_rcv,
-};
-
-static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev)
-{
-       ipvlan->dev->mtu = dev->mtu;
-}
-
-static int ipvlan_register_nf_hook(struct net *net)
-{
-       struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
-       int err = 0;
-
-       if (!vnet->ipvl_nf_hook_refcnt) {
-               err = nf_register_net_hooks(net, ipvl_nfops,
-                                           ARRAY_SIZE(ipvl_nfops));
-               if (!err)
-                       vnet->ipvl_nf_hook_refcnt = 1;
-       } else {
-               vnet->ipvl_nf_hook_refcnt++;
-       }
-
-       return err;
-}
-
-static void ipvlan_unregister_nf_hook(struct net *net)
-{
-       struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
-
-       if (WARN_ON(!vnet->ipvl_nf_hook_refcnt))
-               return;
-
-       vnet->ipvl_nf_hook_refcnt--;
-       if (!vnet->ipvl_nf_hook_refcnt)
-               nf_unregister_net_hooks(net, ipvl_nfops,
-                                       ARRAY_SIZE(ipvl_nfops));
-}
-
 static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,
                                struct netlink_ext_ack *extack)
 {
        struct ipvl_dev *ipvlan;
-       struct net_device *mdev = port->dev;
        unsigned int flags;
        int err;
 
@@ -97,17 +34,12 @@ static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,
                }
                if (nval == IPVLAN_MODE_L3S) {
                        /* New mode is L3S */
-                       err = ipvlan_register_nf_hook(read_pnet(&port->pnet));
-                       if (!err) {
-                               mdev->l3mdev_ops = &ipvl_l3mdev_ops;
-                               mdev->priv_flags |= IFF_L3MDEV_MASTER;
-                       } else
+                       err = ipvlan_l3s_register(port);
+                       if (err)
                                goto fail;
                } else if (port->mode == IPVLAN_MODE_L3S) {
                        /* Old mode was L3S */
-                       mdev->priv_flags &= ~IFF_L3MDEV_MASTER;
-                       ipvlan_unregister_nf_hook(read_pnet(&port->pnet));
-                       mdev->l3mdev_ops = NULL;
+                       ipvlan_l3s_unregister(port);
                }
                port->mode = nval;
        }
@@ -166,11 +98,8 @@ static void ipvlan_port_destroy(struct net_device *dev)
        struct ipvl_port *port = ipvlan_port_get_rtnl(dev);
        struct sk_buff *skb;
 
-       if (port->mode == IPVLAN_MODE_L3S) {
-               dev->priv_flags &= ~IFF_L3MDEV_MASTER;
-               ipvlan_unregister_nf_hook(dev_net(dev));
-               dev->l3mdev_ops = NULL;
-       }
+       if (port->mode == IPVLAN_MODE_L3S)
+               ipvlan_l3s_unregister(port);
        netdev_rx_handler_unregister(dev);
        cancel_work_sync(&port->wq);
        while ((skb = __skb_dequeue(&port->backlog)) != NULL) {
@@ -446,6 +375,11 @@ static const struct header_ops ipvlan_header_ops = {
        .cache_update   = eth_header_cache_update,
 };
 
+static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev)
+{
+       ipvlan->dev->mtu = dev->mtu;
+}
+
 static bool netif_is_ipvlan(const struct net_device *dev)
 {
        /* both ipvlan and ipvtap devices use the same netdev_ops */
@@ -781,7 +715,6 @@ static int ipvlan_device_event(struct notifier_block *unused,
 
        case NETDEV_REGISTER: {
                struct net *oldnet, *newnet = dev_net(dev);
-               struct ipvlan_netns *old_vnet;
 
                oldnet = read_pnet(&port->pnet);
                if (net_eq(newnet, oldnet))
@@ -789,12 +722,7 @@ static int ipvlan_device_event(struct notifier_block *unused,
 
                write_pnet(&port->pnet, newnet);
 
-               old_vnet = net_generic(oldnet, ipvlan_netid);
-               if (!old_vnet->ipvl_nf_hook_refcnt)
-                       break;
-
-               ipvlan_register_nf_hook(newnet);
-               ipvlan_unregister_nf_hook(oldnet);
+               ipvlan_migrate_l3s_hook(oldnet, newnet);
                break;
        }
        case NETDEV_UNREGISTER:
@@ -1068,23 +996,6 @@ static struct notifier_block ipvlan_addr6_vtor_notifier_block __read_mostly = {
 };
 #endif
 
-static void ipvlan_ns_exit(struct net *net)
-{
-       struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
-
-       if (WARN_ON_ONCE(vnet->ipvl_nf_hook_refcnt)) {
-               vnet->ipvl_nf_hook_refcnt = 0;
-               nf_unregister_net_hooks(net, ipvl_nfops,
-                                       ARRAY_SIZE(ipvl_nfops));
-       }
-}
-
-static struct pernet_operations ipvlan_net_ops = {
-       .id = &ipvlan_netid,
-       .size = sizeof(struct ipvlan_netns),
-       .exit = ipvlan_ns_exit,
-};
-
 static int __init ipvlan_init_module(void)
 {
        int err;
@@ -1099,13 +1010,13 @@ static int __init ipvlan_init_module(void)
        register_inetaddr_notifier(&ipvlan_addr4_notifier_block);
        register_inetaddr_validator_notifier(&ipvlan_addr4_vtor_notifier_block);
 
-       err = register_pernet_subsys(&ipvlan_net_ops);
+       err = ipvlan_l3s_init();
        if (err < 0)
                goto error;
 
        err = ipvlan_link_register(&ipvlan_link_ops);
        if (err < 0) {
-               unregister_pernet_subsys(&ipvlan_net_ops);
+               ipvlan_l3s_cleanup();
                goto error;
        }
 
@@ -1126,7 +1037,7 @@ error:
 static void __exit ipvlan_cleanup_module(void)
 {
        rtnl_link_unregister(&ipvlan_link_ops);
-       unregister_pernet_subsys(&ipvlan_net_ops);
+       ipvlan_l3s_cleanup();
        unregister_netdevice_notifier(&ipvlan_notifier_block);
        unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
        unregister_inetaddr_validator_notifier(
index cf22a79..26e5383 100644 (file)
@@ -119,8 +119,6 @@ static struct macvlan_port *macvlan_port_get_rtnl(const struct net_device *dev)
        return rtnl_dereference(dev->rx_handler_data);
 }
 
-#define macvlan_port_exists(dev) (dev->priv_flags & IFF_MACVLAN_PORT)
-
 static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port,
                                               const unsigned char *addr)
 {
@@ -1378,7 +1376,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
        if (!tb[IFLA_ADDRESS])
                eth_hw_addr_random(dev);
 
-       if (!macvlan_port_exists(lowerdev)) {
+       if (!netif_is_macvlan_port(lowerdev)) {
                err = macvlan_port_create(lowerdev);
                if (err < 0)
                        return err;
@@ -1638,7 +1636,7 @@ static int macvlan_device_event(struct notifier_block *unused,
        struct macvlan_port *port;
        LIST_HEAD(list_kill);
 
-       if (!macvlan_port_exists(dev))
+       if (!netif_is_macvlan_port(dev))
                return NOTIFY_DONE;
 
        port = macvlan_port_get_rtnl(dev);
index 172b271..f92c434 100644 (file)
@@ -248,7 +248,7 @@ static int nsim_bpf_create_prog(struct netdevsim *ns, struct bpf_prog *prog)
 
 static int nsim_bpf_verifier_prep(struct bpf_prog *prog)
 {
-       struct netdevsim *ns = netdev_priv(prog->aux->offload->netdev);
+       struct netdevsim *ns = bpf_offload_dev_priv(prog->aux->offload->offdev);
 
        if (!ns->bpf_bind_accept)
                return -EOPNOTSUPP;
@@ -589,7 +589,8 @@ int nsim_bpf_init(struct netdevsim *ns)
                if (IS_ERR_OR_NULL(ns->sdev->ddir_bpf_bound_progs))
                        return -ENOMEM;
 
-               ns->sdev->bpf_dev = bpf_offload_dev_create(&nsim_bpf_dev_ops);
+               ns->sdev->bpf_dev = bpf_offload_dev_create(&nsim_bpf_dev_ops,
+                                                          ns);
                err = PTR_ERR_OR_ZERO(ns->sdev->bpf_dev);
                if (err)
                        return err;
index 8d8e2b3..75a50b5 100644 (file)
@@ -22,7 +22,6 @@
 #include <net/netlink.h>
 #include <net/pkt_cls.h>
 #include <net/rtnetlink.h>
-#include <net/switchdev.h>
 
 #include "netdevsim.h"
 
@@ -148,26 +147,16 @@ static struct device_type nsim_dev_type = {
        .release = nsim_dev_release,
 };
 
-static int
-nsim_port_attr_get(struct net_device *dev, struct switchdev_attr *attr)
+static int nsim_get_port_parent_id(struct net_device *dev,
+                                  struct netdev_phys_item_id *ppid)
 {
        struct netdevsim *ns = netdev_priv(dev);
 
-       switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = sizeof(ns->sdev->switch_id);
-               memcpy(&attr->u.ppid.id, &ns->sdev->switch_id,
-                      attr->u.ppid.id_len);
-               return 0;
-       default:
-               return -EOPNOTSUPP;
-       }
+       ppid->id_len = sizeof(ns->sdev->switch_id);
+       memcpy(&ppid->id, &ns->sdev->switch_id, ppid->id_len);
+       return 0;
 }
 
-static const struct switchdev_ops nsim_switchdev_ops = {
-       .switchdev_port_attr_get        = nsim_port_attr_get,
-};
-
 static int nsim_init(struct net_device *dev)
 {
        char sdev_ddir_name[10], sdev_link_name[32];
@@ -214,7 +203,6 @@ static int nsim_init(struct net_device *dev)
                goto err_bpf_uninit;
 
        SET_NETDEV_DEV(dev, &ns->dev);
-       SWITCHDEV_SET_OPS(dev, &nsim_switchdev_ops);
 
        err = nsim_devlink_setup(ns);
        if (err)
@@ -493,6 +481,7 @@ 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_get_port_parent_id = nsim_get_port_parent_id,
 };
 
 static void nsim_setup(struct net_device *dev)
index 0578fe7..0f772a4 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/delay.h>
-#include <linux/mii.h>
-#include <linux/ethtool.h>
 #include <linux/phy.h>
-#include <linux/mdio.h>
 
 #define PHY_ID_AQ1202  0x03a1b445
 #define PHY_ID_AQ2104  0x03a1b460
 #define PHY_ID_AQR105  0x03a1b4a2
 #define PHY_ID_AQR106  0x03a1b4d0
 #define PHY_ID_AQR107  0x03a1b4e0
+#define PHY_ID_AQCS109 0x03a1b5c2
 #define PHY_ID_AQR405  0x03a1b4b0
 
-static int aquantia_config_aneg(struct phy_device *phydev)
+#define MDIO_AN_TX_VEND_STATUS1                        0xc800
+#define MDIO_AN_TX_VEND_STATUS1_10BASET                (0x0 << 1)
+#define MDIO_AN_TX_VEND_STATUS1_100BASETX      (0x1 << 1)
+#define MDIO_AN_TX_VEND_STATUS1_1000BASET      (0x2 << 1)
+#define MDIO_AN_TX_VEND_STATUS1_10GBASET       (0x3 << 1)
+#define MDIO_AN_TX_VEND_STATUS1_2500BASET      (0x4 << 1)
+#define MDIO_AN_TX_VEND_STATUS1_5000BASET      (0x5 << 1)
+#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK      (0x7 << 1)
+#define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX    BIT(0)
+
+#define MDIO_AN_TX_VEND_INT_STATUS2            0xcc01
+
+#define MDIO_AN_TX_VEND_INT_MASK2              0xd401
+#define MDIO_AN_TX_VEND_INT_MASK2_LINK         BIT(0)
+
+/* Vendor specific 1, MDIO_MMD_VEND1 */
+#define VEND1_GLOBAL_INT_STD_STATUS            0xfc00
+#define VEND1_GLOBAL_INT_VEND_STATUS           0xfc01
+
+#define VEND1_GLOBAL_INT_STD_MASK              0xff00
+#define VEND1_GLOBAL_INT_STD_MASK_PMA1         BIT(15)
+#define VEND1_GLOBAL_INT_STD_MASK_PMA2         BIT(14)
+#define VEND1_GLOBAL_INT_STD_MASK_PCS1         BIT(13)
+#define VEND1_GLOBAL_INT_STD_MASK_PCS2         BIT(12)
+#define VEND1_GLOBAL_INT_STD_MASK_PCS3         BIT(11)
+#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1      BIT(10)
+#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2      BIT(9)
+#define VEND1_GLOBAL_INT_STD_MASK_AN1          BIT(8)
+#define VEND1_GLOBAL_INT_STD_MASK_AN2          BIT(7)
+#define VEND1_GLOBAL_INT_STD_MASK_GBE          BIT(6)
+#define VEND1_GLOBAL_INT_STD_MASK_ALL          BIT(0)
+
+#define VEND1_GLOBAL_INT_VEND_MASK             0xff01
+#define VEND1_GLOBAL_INT_VEND_MASK_PMA         BIT(15)
+#define VEND1_GLOBAL_INT_VEND_MASK_PCS         BIT(14)
+#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS      BIT(13)
+#define VEND1_GLOBAL_INT_VEND_MASK_AN          BIT(12)
+#define VEND1_GLOBAL_INT_VEND_MASK_GBE         BIT(11)
+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1     BIT(2)
+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2     BIT(1)
+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3     BIT(0)
+
+static int aqr_config_aneg(struct phy_device *phydev)
 {
        linkmode_copy(phydev->supported, phy_10gbit_features);
        linkmode_copy(phydev->advertising, phydev->supported);
@@ -30,44 +70,55 @@ static int aquantia_config_aneg(struct phy_device *phydev)
        return 0;
 }
 
-static int aquantia_config_intr(struct phy_device *phydev)
+static int aqr_config_intr(struct phy_device *phydev)
 {
        int err;
 
        if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
-               err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 1);
+               err = phy_write_mmd(phydev, MDIO_MMD_AN,
+                                   MDIO_AN_TX_VEND_INT_MASK2,
+                                   MDIO_AN_TX_VEND_INT_MASK2_LINK);
                if (err < 0)
                        return err;
 
-               err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 1);
+               err = phy_write_mmd(phydev, MDIO_MMD_VEND1,
+                                   VEND1_GLOBAL_INT_STD_MASK,
+                                   VEND1_GLOBAL_INT_STD_MASK_ALL);
                if (err < 0)
                        return err;
 
-               err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0x1001);
+               err = phy_write_mmd(phydev, MDIO_MMD_VEND1,
+                                   VEND1_GLOBAL_INT_VEND_MASK,
+                                   VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 |
+                                   VEND1_GLOBAL_INT_VEND_MASK_AN);
        } else {
-               err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 0);
+               err = phy_write_mmd(phydev, MDIO_MMD_AN,
+                                   MDIO_AN_TX_VEND_INT_MASK2, 0);
                if (err < 0)
                        return err;
 
-               err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 0);
+               err = phy_write_mmd(phydev, MDIO_MMD_VEND1,
+                                   VEND1_GLOBAL_INT_STD_MASK, 0);
                if (err < 0)
                        return err;
 
-               err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0);
+               err = phy_write_mmd(phydev, MDIO_MMD_VEND1,
+                                   VEND1_GLOBAL_INT_VEND_MASK, 0);
        }
 
        return err;
 }
 
-static int aquantia_ack_interrupt(struct phy_device *phydev)
+static int aqr_ack_interrupt(struct phy_device *phydev)
 {
        int reg;
 
-       reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xcc01);
+       reg = phy_read_mmd(phydev, MDIO_MMD_AN,
+                          MDIO_AN_TX_VEND_INT_STATUS2);
        return (reg < 0) ? reg : 0;
 }
 
-static int aquantia_read_status(struct phy_device *phydev)
+static int aqr_read_status(struct phy_device *phydev)
 {
        int reg;
 
@@ -78,21 +129,20 @@ static int aquantia_read_status(struct phy_device *phydev)
        else
                phydev->link = 0;
 
-       reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
+       reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1);
        mdelay(10);
-       reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
+       reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1);
 
-       switch (reg) {
-       case 0x9:
+       switch (reg & MDIO_AN_TX_VEND_STATUS1_RATE_MASK) {
+       case MDIO_AN_TX_VEND_STATUS1_2500BASET:
                phydev->speed = SPEED_2500;
                break;
-       case 0x5:
+       case MDIO_AN_TX_VEND_STATUS1_1000BASET:
                phydev->speed = SPEED_1000;
                break;
-       case 0x3:
+       case MDIO_AN_TX_VEND_STATUS1_100BASETX:
                phydev->speed = SPEED_100;
                break;
-       case 0x7:
        default:
                phydev->speed = SPEED_10000;
                break;
@@ -102,88 +152,93 @@ static int aquantia_read_status(struct phy_device *phydev)
        return 0;
 }
 
-static struct phy_driver aquantia_driver[] = {
+static struct phy_driver aqr_driver[] = {
 {
-       .phy_id         = PHY_ID_AQ1202,
-       .phy_id_mask    = 0xfffffff0,
+       PHY_ID_MATCH_MODEL(PHY_ID_AQ1202),
        .name           = "Aquantia AQ1202",
        .features       = PHY_10GBIT_FULL_FEATURES,
        .aneg_done      = genphy_c45_aneg_done,
-       .config_aneg    = aquantia_config_aneg,
-       .config_intr    = aquantia_config_intr,
-       .ack_interrupt  = aquantia_ack_interrupt,
-       .read_status    = aquantia_read_status,
+       .config_aneg    = aqr_config_aneg,
+       .config_intr    = aqr_config_intr,
+       .ack_interrupt  = aqr_ack_interrupt,
+       .read_status    = aqr_read_status,
 },
 {
-       .phy_id         = PHY_ID_AQ2104,
-       .phy_id_mask    = 0xfffffff0,
+       PHY_ID_MATCH_MODEL(PHY_ID_AQ2104),
        .name           = "Aquantia AQ2104",
        .features       = PHY_10GBIT_FULL_FEATURES,
        .aneg_done      = genphy_c45_aneg_done,
-       .config_aneg    = aquantia_config_aneg,
-       .config_intr    = aquantia_config_intr,
-       .ack_interrupt  = aquantia_ack_interrupt,
-       .read_status    = aquantia_read_status,
+       .config_aneg    = aqr_config_aneg,
+       .config_intr    = aqr_config_intr,
+       .ack_interrupt  = aqr_ack_interrupt,
+       .read_status    = aqr_read_status,
 },
 {
-       .phy_id         = PHY_ID_AQR105,
-       .phy_id_mask    = 0xfffffff0,
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR105),
        .name           = "Aquantia AQR105",
        .features       = PHY_10GBIT_FULL_FEATURES,
        .aneg_done      = genphy_c45_aneg_done,
-       .config_aneg    = aquantia_config_aneg,
-       .config_intr    = aquantia_config_intr,
-       .ack_interrupt  = aquantia_ack_interrupt,
-       .read_status    = aquantia_read_status,
+       .config_aneg    = aqr_config_aneg,
+       .config_intr    = aqr_config_intr,
+       .ack_interrupt  = aqr_ack_interrupt,
+       .read_status    = aqr_read_status,
 },
 {
-       .phy_id         = PHY_ID_AQR106,
-       .phy_id_mask    = 0xfffffff0,
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR106),
        .name           = "Aquantia AQR106",
        .features       = PHY_10GBIT_FULL_FEATURES,
        .aneg_done      = genphy_c45_aneg_done,
-       .config_aneg    = aquantia_config_aneg,
-       .config_intr    = aquantia_config_intr,
-       .ack_interrupt  = aquantia_ack_interrupt,
-       .read_status    = aquantia_read_status,
+       .config_aneg    = aqr_config_aneg,
+       .config_intr    = aqr_config_intr,
+       .ack_interrupt  = aqr_ack_interrupt,
+       .read_status    = aqr_read_status,
 },
 {
-       .phy_id         = PHY_ID_AQR107,
-       .phy_id_mask    = 0xfffffff0,
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR107),
        .name           = "Aquantia AQR107",
        .features       = PHY_10GBIT_FULL_FEATURES,
        .aneg_done      = genphy_c45_aneg_done,
-       .config_aneg    = aquantia_config_aneg,
-       .config_intr    = aquantia_config_intr,
-       .ack_interrupt  = aquantia_ack_interrupt,
-       .read_status    = aquantia_read_status,
+       .config_aneg    = aqr_config_aneg,
+       .config_intr    = aqr_config_intr,
+       .ack_interrupt  = aqr_ack_interrupt,
+       .read_status    = aqr_read_status,
 },
 {
-       .phy_id         = PHY_ID_AQR405,
-       .phy_id_mask    = 0xfffffff0,
+       PHY_ID_MATCH_MODEL(PHY_ID_AQCS109),
+       .name           = "Aquantia AQCS109",
+       .features       = PHY_10GBIT_FULL_FEATURES,
+       .aneg_done      = genphy_c45_aneg_done,
+       .config_aneg    = aqr_config_aneg,
+       .config_intr    = aqr_config_intr,
+       .ack_interrupt  = aqr_ack_interrupt,
+       .read_status    = aqr_read_status,
+},
+{
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR405),
        .name           = "Aquantia AQR405",
        .features       = PHY_10GBIT_FULL_FEATURES,
        .aneg_done      = genphy_c45_aneg_done,
-       .config_aneg    = aquantia_config_aneg,
-       .config_intr    = aquantia_config_intr,
-       .ack_interrupt  = aquantia_ack_interrupt,
-       .read_status    = aquantia_read_status,
+       .config_aneg    = aqr_config_aneg,
+       .config_intr    = aqr_config_intr,
+       .ack_interrupt  = aqr_ack_interrupt,
+       .read_status    = aqr_read_status,
 },
 };
 
-module_phy_driver(aquantia_driver);
+module_phy_driver(aqr_driver);
 
-static struct mdio_device_id __maybe_unused aquantia_tbl[] = {
-       { PHY_ID_AQ1202, 0xfffffff0 },
-       { PHY_ID_AQ2104, 0xfffffff0 },
-       { PHY_ID_AQR105, 0xfffffff0 },
-       { PHY_ID_AQR106, 0xfffffff0 },
-       { PHY_ID_AQR107, 0xfffffff0 },
-       { PHY_ID_AQR405, 0xfffffff0 },
+static struct mdio_device_id __maybe_unused aqr_tbl[] = {
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQ2104) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
        { }
 };
 
-MODULE_DEVICE_TABLE(mdio, aquantia_tbl);
+MODULE_DEVICE_TABLE(mdio, aqr_tbl);
 
 MODULE_DESCRIPTION("Aquantia PHY driver");
 MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
index 25ef483..2fe2eba 100644 (file)
@@ -885,14 +885,14 @@ static void decode_txts(struct dp83640_private *dp83640,
                        struct phy_txts *phy_txts)
 {
        struct skb_shared_hwtstamps shhwtstamps;
+       struct dp83640_skb_info *skb_info;
        struct sk_buff *skb;
-       u64 ns;
        u8 overflow;
+       u64 ns;
 
        /* We must already have the skb that triggered this. */
-
+again:
        skb = skb_dequeue(&dp83640->tx_queue);
-
        if (!skb) {
                pr_debug("have timestamp but tx_queue empty\n");
                return;
@@ -907,6 +907,11 @@ static void decode_txts(struct dp83640_private *dp83640,
                }
                return;
        }
+       skb_info = (struct dp83640_skb_info *)skb->cb;
+       if (time_after(jiffies, skb_info->tmo)) {
+               kfree_skb(skb);
+               goto again;
+       }
 
        ns = phy2txts(phy_txts);
        memset(&shhwtstamps, 0, sizeof(shhwtstamps));
@@ -1459,6 +1464,7 @@ static bool dp83640_rxtstamp(struct phy_device *phydev,
 static void dp83640_txtstamp(struct phy_device *phydev,
                             struct sk_buff *skb, int type)
 {
+       struct dp83640_skb_info *skb_info = (struct dp83640_skb_info *)skb->cb;
        struct dp83640_private *dp83640 = phydev->priv;
 
        switch (dp83640->hwts_tx_en) {
@@ -1471,6 +1477,7 @@ static void dp83640_txtstamp(struct phy_device *phydev,
                /* fall through */
        case HWTSTAMP_TX_ON:
                skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+               skb_info->tmo = jiffies + SKB_TIMESTAMP_TIMEOUT;
                skb_queue_tail(&dp83640->tx_queue, skb);
                break;
 
index 8a8d9f6..fc09c5c 100644 (file)
@@ -127,17 +127,13 @@ static int dp83867_config_port_mirroring(struct phy_device *phydev)
 {
        struct dp83867_private *dp83867 =
                (struct dp83867_private *)phydev->priv;
-       u16 val;
-
-       val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4);
 
        if (dp83867->port_mirroring == DP83867_PORT_MIRROING_EN)
-               val |= DP83867_CFG4_PORT_MIRROR_EN;
+               phy_set_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
+                                DP83867_CFG4_PORT_MIRROR_EN);
        else
-               val &= ~DP83867_CFG4_PORT_MIRROR_EN;
-
-       phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val);
-
+               phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
+                                  DP83867_CFG4_PORT_MIRROR_EN);
        return 0;
 }
 
@@ -222,11 +218,9 @@ static int dp83867_config_init(struct phy_device *phydev)
        }
 
        /* RX_DV/RX_CTRL strapped in mode 1 or mode 2 workaround */
-       if (dp83867->rxctrl_strap_quirk) {
-               val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4);
-               val &= ~BIT(7);
-               phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val);
-       }
+       if (dp83867->rxctrl_strap_quirk)
+               phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
+                                  BIT(7));
 
        if (phy_interface_is_rgmii(phydev)) {
                val = phy_read(phydev, MII_DP83867_PHYCTRL);
@@ -275,17 +269,11 @@ static int dp83867_config_init(struct phy_device *phydev)
                phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL,
                              delay);
 
-               if (dp83867->io_impedance >= 0) {
-                       val = phy_read_mmd(phydev, DP83867_DEVADDR,
-                                          DP83867_IO_MUX_CFG);
-
-                       val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL;
-                       val |= dp83867->io_impedance &
-                              DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL;
-
-                       phy_write_mmd(phydev, DP83867_DEVADDR,
-                                     DP83867_IO_MUX_CFG, val);
-               }
+               if (dp83867->io_impedance >= 0)
+                       phy_modify_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG,
+                                      DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL,
+                                      dp83867->io_impedance &
+                                      DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL);
        }
 
        /* Enable Interrupt output INT_OE in CFG3 register */
@@ -299,12 +287,11 @@ static int dp83867_config_init(struct phy_device *phydev)
                dp83867_config_port_mirroring(phydev);
 
        /* Clock output selection if muxing property is set */
-       if (dp83867->clk_output_sel != DP83867_CLK_O_SEL_REF_CLK) {
-               val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG);
-               val &= ~DP83867_IO_MUX_CFG_CLK_O_SEL_MASK;
-               val |= (dp83867->clk_output_sel << DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT);
-               phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG, val);
-       }
+       if (dp83867->clk_output_sel != DP83867_CLK_O_SEL_REF_CLK)
+               phy_modify_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG,
+                              DP83867_IO_MUX_CFG_CLK_O_SEL_MASK,
+                              dp83867->clk_output_sel <<
+                              DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT);
 
        return 0;
 }
index da13356..e9704af 100644 (file)
@@ -144,11 +144,8 @@ static int dp83811_set_wol(struct phy_device *phydev,
                phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
                              value);
        } else {
-               value = phy_read_mmd(phydev, DP83811_DEVADDR,
-                                    MII_DP83811_WOL_CFG);
-               value &= ~DP83811_WOL_EN;
-               phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
-                             value);
+               phy_clear_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
+                                  DP83811_WOL_EN);
        }
 
        return 0;
@@ -328,14 +325,10 @@ static int dp83811_suspend(struct phy_device *phydev)
 
 static int dp83811_resume(struct phy_device *phydev)
 {
-       int value;
-
        genphy_resume(phydev);
 
-       value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);
-
-       phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG, value |
-                     DP83811_WOL_CLR_INDICATION);
+       phy_set_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
+                        DP83811_WOL_CLR_INDICATION);
 
        return 0;
 }
index 47a8cb5..b0d1368 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/seqlock.h>
 #include <linux/idr.h>
 #include <linux/netdevice.h>
@@ -38,7 +38,7 @@ struct fixed_phy {
        bool no_carrier;
        int (*link_update)(struct net_device *, struct fixed_phy_status *);
        struct list_head node;
-       int link_gpio;
+       struct gpio_desc *link_gpiod;
 };
 
 static struct platform_device *pdev;
@@ -67,8 +67,8 @@ EXPORT_SYMBOL_GPL(fixed_phy_change_carrier);
 
 static void fixed_phy_update(struct fixed_phy *fp)
 {
-       if (!fp->no_carrier && gpio_is_valid(fp->link_gpio))
-               fp->status.link = !!gpio_get_value_cansleep(fp->link_gpio);
+       if (!fp->no_carrier && fp->link_gpiod)
+               fp->status.link = !!gpiod_get_value_cansleep(fp->link_gpiod);
 }
 
 static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
@@ -85,11 +85,11 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
                                s = read_seqcount_begin(&fp->seqcount);
                                fp->status.link = !fp->no_carrier;
                                /* Issue callback if user registered it. */
-                               if (fp->link_update) {
+                               if (fp->link_update)
                                        fp->link_update(fp->phydev->attached_dev,
                                                        &fp->status);
-                                       fixed_phy_update(fp);
-                               }
+                               /* Check the GPIO for change in status */
+                               fixed_phy_update(fp);
                                state = fp->status;
                        } while (read_seqcount_retry(&fp->seqcount, s));
 
@@ -133,9 +133,9 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
 }
 EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
 
-int fixed_phy_add(unsigned int irq, int phy_addr,
-                 struct fixed_phy_status *status,
-                 int link_gpio)
+static int fixed_phy_add_gpiod(unsigned int irq, int phy_addr,
+                              struct fixed_phy_status *status,
+                              struct gpio_desc *gpiod)
 {
        int ret;
        struct fixed_mdio_bus *fmb = &platform_fmb;
@@ -156,24 +156,19 @@ int fixed_phy_add(unsigned int irq, int phy_addr,
 
        fp->addr = phy_addr;
        fp->status = *status;
-       fp->link_gpio = link_gpio;
-
-       if (gpio_is_valid(fp->link_gpio)) {
-               ret = gpio_request_one(fp->link_gpio, GPIOF_DIR_IN,
-                                      "fixed-link-gpio-link");
-               if (ret)
-                       goto err_regs;
-       }
+       fp->link_gpiod = gpiod;
 
        fixed_phy_update(fp);
 
        list_add_tail(&fp->node, &fmb->phys);
 
        return 0;
+}
 
-err_regs:
-       kfree(fp);
-       return ret;
+int fixed_phy_add(unsigned int irq, int phy_addr,
+                 struct fixed_phy_status *status) {
+
+       return fixed_phy_add_gpiod(irq, phy_addr, status, NULL);
 }
 EXPORT_SYMBOL_GPL(fixed_phy_add);
 
@@ -187,8 +182,8 @@ static void fixed_phy_del(int phy_addr)
        list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
                if (fp->addr == phy_addr) {
                        list_del(&fp->node);
-                       if (gpio_is_valid(fp->link_gpio))
-                               gpio_free(fp->link_gpio);
+                       if (fp->link_gpiod)
+                               gpiod_put(fp->link_gpiod);
                        kfree(fp);
                        ida_simple_remove(&phy_fixed_ida, phy_addr);
                        return;
@@ -196,10 +191,48 @@ static void fixed_phy_del(int phy_addr)
        }
 }
 
-struct phy_device *fixed_phy_register(unsigned int irq,
-                                     struct fixed_phy_status *status,
-                                     int link_gpio,
-                                     struct device_node *np)
+#ifdef CONFIG_OF_GPIO
+static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np)
+{
+       struct device_node *fixed_link_node;
+       struct gpio_desc *gpiod;
+
+       if (!np)
+               return NULL;
+
+       fixed_link_node = of_get_child_by_name(np, "fixed-link");
+       if (!fixed_link_node)
+               return NULL;
+
+       /*
+        * As the fixed link is just a device tree node without any
+        * Linux device associated with it, we simply have obtain
+        * the GPIO descriptor from the device tree like this.
+        */
+       gpiod = gpiod_get_from_of_node(fixed_link_node, "link-gpios", 0,
+                                      GPIOD_IN, "mdio");
+       of_node_put(fixed_link_node);
+       if (IS_ERR(gpiod)) {
+               if (PTR_ERR(gpiod) == -EPROBE_DEFER)
+                       return gpiod;
+               pr_err("error getting GPIO for fixed link %pOF, proceed without\n",
+                      fixed_link_node);
+               gpiod = NULL;
+       }
+
+       return gpiod;
+}
+#else
+static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np)
+{
+       return NULL;
+}
+#endif
+
+static struct phy_device *__fixed_phy_register(unsigned int irq,
+                                              struct fixed_phy_status *status,
+                                              struct device_node *np,
+                                              struct gpio_desc *gpiod)
 {
        struct fixed_mdio_bus *fmb = &platform_fmb;
        struct phy_device *phy;
@@ -209,12 +242,19 @@ struct phy_device *fixed_phy_register(unsigned int irq,
        if (!fmb->mii_bus || fmb->mii_bus->state != MDIOBUS_REGISTERED)
                return ERR_PTR(-EPROBE_DEFER);
 
+       /* Check if we have a GPIO associated with this fixed phy */
+       if (!gpiod) {
+               gpiod = fixed_phy_get_gpiod(np);
+               if (IS_ERR(gpiod))
+                       return ERR_CAST(gpiod);
+       }
+
        /* Get the next available PHY address, up to PHY_MAX_ADDR */
        phy_addr = ida_simple_get(&phy_fixed_ida, 0, PHY_MAX_ADDR, GFP_KERNEL);
        if (phy_addr < 0)
                return ERR_PTR(phy_addr);
 
-       ret = fixed_phy_add(irq, phy_addr, status, link_gpio);
+       ret = fixed_phy_add_gpiod(irq, phy_addr, status, gpiod);
        if (ret < 0) {
                ida_simple_remove(&phy_fixed_ida, phy_addr);
                return ERR_PTR(ret);
@@ -270,8 +310,24 @@ struct phy_device *fixed_phy_register(unsigned int irq,
 
        return phy;
 }
+
+struct phy_device *fixed_phy_register(unsigned int irq,
+                                     struct fixed_phy_status *status,
+                                     struct device_node *np)
+{
+       return __fixed_phy_register(irq, status, np, NULL);
+}
 EXPORT_SYMBOL_GPL(fixed_phy_register);
 
+struct phy_device *
+fixed_phy_register_with_gpiod(unsigned int irq,
+                             struct fixed_phy_status *status,
+                             struct gpio_desc *gpiod)
+{
+       return __fixed_phy_register(irq, status, NULL, gpiod);
+}
+EXPORT_SYMBOL_GPL(fixed_phy_register_with_gpiod);
+
 void fixed_phy_unregister(struct phy_device *phy)
 {
        phy_device_remove(phy);
index 90f44ba..3ccba37 100644 (file)
@@ -842,7 +842,6 @@ static int m88e1510_config_init(struct phy_device *phydev)
 
        /* SGMII-to-Copper mode initialization */
        if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
-
                /* Select page 18 */
                err = marvell_set_page(phydev, 18);
                if (err < 0)
@@ -865,21 +864,6 @@ static int m88e1510_config_init(struct phy_device *phydev)
                err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
                if (err < 0)
                        return err;
-
-               /* There appears to be a bug in the 88e1512 when used in
-                * SGMII to copper mode, where the AN advertisement register
-                * clears the pause bits each time a negotiation occurs.
-                * This means we can never be truely sure what was advertised,
-                * so disable Pause support.
-                */
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
-                                  phydev->supported);
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
-                                  phydev->supported);
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
-                                  phydev->advertising);
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
-                                  phydev->advertising);
        }
 
        return m88e1318_config_init(phydev);
index 38cfc92..f9e0a2f 100644 (file)
@@ -58,24 +58,6 @@ struct mv3310_priv {
        char *hwmon_name;
 };
 
-static int mv3310_modify(struct phy_device *phydev, int devad, u16 reg,
-                        u16 mask, u16 bits)
-{
-       int old, val, ret;
-
-       old = phy_read_mmd(phydev, devad, reg);
-       if (old < 0)
-               return old;
-
-       val = (old & ~mask) | (bits & mask);
-       if (val == old)
-               return 0;
-
-       ret = phy_write_mmd(phydev, devad, reg, val);
-
-       return ret < 0 ? ret : 1;
-}
-
 #ifdef CONFIG_HWMON
 static umode_t mv3310_hwmon_is_visible(const void *data,
                                       enum hwmon_sensor_types type,
@@ -159,10 +141,9 @@ static int mv3310_hwmon_config(struct phy_device *phydev, bool enable)
                return ret;
 
        val = enable ? MV_V2_TEMP_CTRL_SAMPLE : MV_V2_TEMP_CTRL_DISABLE;
-       ret = mv3310_modify(phydev, MDIO_MMD_VEND2, MV_V2_TEMP_CTRL,
-                           MV_V2_TEMP_CTRL_MASK, val);
 
-       return ret < 0 ? ret : 0;
+       return phy_modify_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP_CTRL,
+                             MV_V2_TEMP_CTRL_MASK, val);
 }
 
 static void mv3310_hwmon_disable(void *data)
@@ -252,8 +233,7 @@ static int mv3310_resume(struct phy_device *phydev)
 
 static int mv3310_config_init(struct phy_device *phydev)
 {
-       __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
-       int val;
+       int ret, val;
 
        /* Check that the PHY interface type is compatible */
        if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
@@ -262,83 +242,19 @@ static int mv3310_config_init(struct phy_device *phydev)
            phydev->interface != PHY_INTERFACE_MODE_10GKR)
                return -ENODEV;
 
-       __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, supported);
-       __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, supported);
-
        if (phydev->c45_ids.devices_in_package & MDIO_DEVS_AN) {
                val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
                if (val < 0)
                        return val;
 
                if (val & MDIO_AN_STAT1_ABLE)
-                       __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, supported);
-       }
-
-       val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT2);
-       if (val < 0)
-               return val;
-
-       /* Ethtool does not support the WAN mode bits */
-       if (val & (MDIO_PMA_STAT2_10GBSR | MDIO_PMA_STAT2_10GBLR |
-                  MDIO_PMA_STAT2_10GBER | MDIO_PMA_STAT2_10GBLX4 |
-                  MDIO_PMA_STAT2_10GBSW | MDIO_PMA_STAT2_10GBLW |
-                  MDIO_PMA_STAT2_10GBEW))
-               __set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, supported);
-       if (val & MDIO_PMA_STAT2_10GBSR)
-               __set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, supported);
-       if (val & MDIO_PMA_STAT2_10GBLR)
-               __set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, supported);
-       if (val & MDIO_PMA_STAT2_10GBER)
-               __set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, supported);
-
-       if (val & MDIO_PMA_STAT2_EXTABLE) {
-               val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE);
-               if (val < 0)
-                       return val;
-
-               if (val & (MDIO_PMA_EXTABLE_10GBT | MDIO_PMA_EXTABLE_1000BT |
-                          MDIO_PMA_EXTABLE_100BTX | MDIO_PMA_EXTABLE_10BT))
-                       __set_bit(ETHTOOL_LINK_MODE_TP_BIT, supported);
-               if (val & MDIO_PMA_EXTABLE_10GBLRM)
-                       __set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, supported);
-               if (val & (MDIO_PMA_EXTABLE_10GBKX4 | MDIO_PMA_EXTABLE_10GBKR |
-                          MDIO_PMA_EXTABLE_1000BKX))
-                       __set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, supported);
-               if (val & MDIO_PMA_EXTABLE_10GBLRM)
-                       __set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
-                                 supported);
-               if (val & MDIO_PMA_EXTABLE_10GBT)
-                       __set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
-                                 supported);
-               if (val & MDIO_PMA_EXTABLE_10GBKX4)
-                       __set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
-                                 supported);
-               if (val & MDIO_PMA_EXTABLE_10GBKR)
-                       __set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
-                                 supported);
-               if (val & MDIO_PMA_EXTABLE_1000BT)
-                       __set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-                                 supported);
-               if (val & MDIO_PMA_EXTABLE_1000BKX)
-                       __set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
-                                 supported);
-               if (val & MDIO_PMA_EXTABLE_100BTX) {
-                       __set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
-                                 supported);
-                       __set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
-                                 supported);
-               }
-               if (val & MDIO_PMA_EXTABLE_10BT) {
-                       __set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
-                                 supported);
-                       __set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
-                                 supported);
-               }
+                       __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                                 phydev->supported);
        }
 
-       linkmode_copy(phydev->supported, supported);
-       linkmode_and(phydev->advertising, phydev->advertising,
-                    phydev->supported);
+       ret = genphy_c45_pma_read_abilities(phydev);
+       if (ret)
+               return ret;
 
        return 0;
 }
@@ -360,39 +276,32 @@ static int mv3310_config_aneg(struct phy_device *phydev)
                return genphy_c45_an_disable_aneg(phydev);
        }
 
-       linkmode_and(phydev->advertising, phydev->advertising,
-                    phydev->supported);
-
-       ret = mv3310_modify(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE,
-                           ADVERTISE_ALL | ADVERTISE_100BASE4 |
-                           ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
-                           linkmode_adv_to_mii_adv_t(phydev->advertising));
+       ret = genphy_c45_an_config_aneg(phydev);
        if (ret < 0)
                return ret;
        if (ret > 0)
                changed = true;
 
+       /* Clause 45 has no standardized support for 1000BaseT, therefore
+        * use vendor registers for this mode.
+        */
        reg = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
-       ret = mv3310_modify(phydev, MDIO_MMD_AN, MV_AN_CTRL1000,
-                           ADVERTISE_1000FULL | ADVERTISE_1000HALF, reg);
+       ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MV_AN_CTRL1000,
+                            ADVERTISE_1000FULL | ADVERTISE_1000HALF, reg);
        if (ret < 0)
                return ret;
        if (ret > 0)
                changed = true;
 
-       /* 10G control register */
-       if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
-                             phydev->advertising))
-               reg = MDIO_AN_10GBT_CTRL_ADV10G;
-       else
-               reg = 0;
+       if (!changed) {
+               /* Configure and restart aneg if it wasn't set before */
+               ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
+               if (ret < 0)
+                       return ret;
 
-       ret = mv3310_modify(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
-                           MDIO_AN_10GBT_CTRL_ADV10G, reg);
-       if (ret < 0)
-               return ret;
-       if (ret > 0)
-               changed = true;
+               if (!(ret & MDIO_AN_CTRL1_ENABLE))
+                       changed = 1;
+       }
 
        if (changed)
                ret = genphy_c45_restart_aneg(phydev);
@@ -446,16 +355,8 @@ static int mv3310_read_10gbr_status(struct phy_device *phydev)
 
 static int mv3310_read_status(struct phy_device *phydev)
 {
-       u32 mmd_mask = phydev->c45_ids.devices_in_package;
        int val;
 
-       /* The vendor devads do not report link status.  Avoid the PHYXS
-        * instance as there are three, and its status depends on the MAC
-        * being appropriately configured for the negotiated speed.
-        */
-       mmd_mask &= ~(BIT(MDIO_MMD_VEND1) | BIT(MDIO_MMD_VEND2) |
-                     BIT(MDIO_MMD_PHYXS));
-
        phydev->speed = SPEED_UNKNOWN;
        phydev->duplex = DUPLEX_UNKNOWN;
        linkmode_zero(phydev->lp_advertising);
@@ -471,12 +372,10 @@ static int mv3310_read_status(struct phy_device *phydev)
        if (val & MDIO_STAT1_LSTATUS)
                return mv3310_read_10gbr_status(phydev);
 
-       val = genphy_c45_read_link(phydev, mmd_mask);
+       val = genphy_c45_read_link(phydev);
        if (val < 0)
                return val;
 
-       phydev->link = val > 0 ? 1 : 0;
-
        val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
        if (val < 0)
                return val;
index 03af927..e17672b 100644 (file)
@@ -47,6 +47,16 @@ int genphy_c45_pma_setup_forced(struct phy_device *phydev)
                /* Assume 1000base-T */
                ctrl2 |= MDIO_PMA_CTRL2_1000BT;
                break;
+       case SPEED_2500:
+               ctrl1 |= MDIO_CTRL1_SPEED2_5G;
+               /* Assume 2.5Gbase-T */
+               ctrl2 |= MDIO_PMA_CTRL2_2_5GBT;
+               break;
+       case SPEED_5000:
+               ctrl1 |= MDIO_CTRL1_SPEED5G;
+               /* Assume 5Gbase-T */
+               ctrl2 |= MDIO_PMA_CTRL2_5GBT;
+               break;
        case SPEED_10000:
                ctrl1 |= MDIO_CTRL1_SPEED10G;
                /* Assume 10Gbase-T */
@@ -65,6 +75,50 @@ int genphy_c45_pma_setup_forced(struct phy_device *phydev)
 EXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced);
 
 /**
+ * genphy_c45_an_config_aneg - configure advertisement registers
+ * @phydev: target phy_device struct
+ *
+ * Configure advertisement registers based on modes set in phydev->advertising
+ *
+ * Returns negative errno code on failure, 0 if advertisement didn't change,
+ * or 1 if advertised modes changed.
+ */
+int genphy_c45_an_config_aneg(struct phy_device *phydev)
+{
+       int changed = 0, ret;
+       u32 adv;
+
+       linkmode_and(phydev->advertising, phydev->advertising,
+                    phydev->supported);
+
+       adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
+
+       ret = phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE,
+                            ADVERTISE_ALL | ADVERTISE_100BASE4 |
+                            ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
+                            adv);
+       if (ret < 0)
+               return ret;
+       if (ret > 0)
+               changed = 1;
+
+       adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising);
+
+       ret = phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+                            MDIO_AN_10GBT_CTRL_ADV10G |
+                            MDIO_AN_10GBT_CTRL_ADV5G |
+                            MDIO_AN_10GBT_CTRL_ADV2_5G,
+                            adv);
+       if (ret < 0)
+               return ret;
+       if (ret > 0)
+               changed = 1;
+
+       return changed;
+}
+EXPORT_SYMBOL_GPL(genphy_c45_an_config_aneg);
+
+/**
  * genphy_c45_an_disable_aneg - disable auto-negotiation
  * @phydev: target phy_device struct
  *
@@ -75,15 +129,9 @@ EXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced);
  */
 int genphy_c45_an_disable_aneg(struct phy_device *phydev)
 {
-       int val;
 
-       val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
-       if (val < 0)
-               return val;
-
-       val &= ~(MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART);
-
-       return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, val);
+       return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1,
+                                 MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART);
 }
 EXPORT_SYMBOL_GPL(genphy_c45_an_disable_aneg);
 
@@ -97,15 +145,8 @@ EXPORT_SYMBOL_GPL(genphy_c45_an_disable_aneg);
  */
 int genphy_c45_restart_aneg(struct phy_device *phydev)
 {
-       int val;
-
-       val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
-       if (val < 0)
-               return val;
-
-       val |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART;
-
-       return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, val);
+       return phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1,
+                               MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART);
 }
 EXPORT_SYMBOL_GPL(genphy_c45_restart_aneg);
 
@@ -131,25 +172,40 @@ EXPORT_SYMBOL_GPL(genphy_c45_aneg_done);
 /**
  * genphy_c45_read_link - read the overall link status from the MMDs
  * @phydev: target phy_device struct
- * @mmd_mask: MMDs to read status from
  *
  * Read the link status from the specified MMDs, and if they all indicate
- * that the link is up, return positive.  If an error is encountered,
+ * that the link is up, set phydev->link to 1.  If an error is encountered,
  * a negative errno will be returned, otherwise zero.
  */
-int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask)
+int genphy_c45_read_link(struct phy_device *phydev)
 {
+       u32 mmd_mask = phydev->c45_ids.devices_in_package;
        int val, devad;
        bool link = true;
 
-       while (mmd_mask) {
+       /* The vendor devads and C22EXT do not report link status. Avoid the
+        * PHYXS instance as its status may depend on the MAC being
+        * appropriately configured for the negotiated speed.
+        */
+       mmd_mask &= ~(MDIO_DEVS_VEND1 | MDIO_DEVS_VEND2 | MDIO_DEVS_C22EXT |
+                     MDIO_DEVS_PHYXS);
+
+       while (mmd_mask && link) {
                devad = __ffs(mmd_mask);
                mmd_mask &= ~BIT(devad);
 
                /* The link state is latched low so that momentary link
-                * drops can be detected.  Do not double-read the status
-                * register if the link is down.
+                * drops can be detected. Do not double-read the status
+                * in polling mode to detect such short link drops.
                 */
+               if (!phy_polling_mode(phydev)) {
+                       val = phy_read_mmd(phydev, devad, MDIO_STAT1);
+                       if (val < 0)
+                               return val;
+                       else if (val & MDIO_STAT1_LSTATUS)
+                               continue;
+               }
+
                val = phy_read_mmd(phydev, devad, MDIO_STAT1);
                if (val < 0)
                        return val;
@@ -158,7 +214,9 @@ int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask)
                        link = false;
        }
 
-       return link;
+       phydev->link = link;
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(genphy_c45_read_link);
 
@@ -190,6 +248,12 @@ int genphy_c45_read_lpa(struct phy_device *phydev)
        if (val < 0)
                return val;
 
+       if (val & MDIO_AN_10GBT_STAT_LP2_5G)
+               linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+                                phydev->lp_advertising);
+       if (val & MDIO_AN_10GBT_STAT_LP5G)
+               linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+                                phydev->lp_advertising);
        if (val & MDIO_AN_10GBT_STAT_LP10G)
                linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
                                 phydev->lp_advertising);
@@ -220,6 +284,12 @@ int genphy_c45_read_pma(struct phy_device *phydev)
        case MDIO_PMA_CTRL1_SPEED1000:
                phydev->speed = SPEED_1000;
                break;
+       case MDIO_CTRL1_SPEED2_5G:
+               phydev->speed = SPEED_2500;
+               break;
+       case MDIO_CTRL1_SPEED5G:
+               phydev->speed = SPEED_5000;
+               break;
        case MDIO_CTRL1_SPEED10G:
                phydev->speed = SPEED_10000;
                break;
@@ -267,6 +337,95 @@ int genphy_c45_read_mdix(struct phy_device *phydev)
 }
 EXPORT_SYMBOL_GPL(genphy_c45_read_mdix);
 
+/**
+ * genphy_c45_pma_read_abilities - read supported link modes from PMA
+ * @phydev: target phy_device struct
+ *
+ * Read the supported link modes from the PMA Status 2 (1.8) register. If bit
+ * 1.8.9 is set, the list of supported modes is build using the values in the
+ * PMA Extended Abilities (1.11) register, indicating 1000BASET an 10G related
+ * modes. If bit 1.11.14 is set, then the list is also extended with the modes
+ * in the 2.5G/5G PMA Extended register (1.21), indicating if 2.5GBASET and
+ * 5GBASET are supported.
+ */
+int genphy_c45_pma_read_abilities(struct phy_device *phydev)
+{
+       int val;
+
+       val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT2);
+       if (val < 0)
+               return val;
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
+                        phydev->supported,
+                        val & MDIO_PMA_STAT2_10GBSR);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
+                        phydev->supported,
+                        val & MDIO_PMA_STAT2_10GBLR);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
+                        phydev->supported,
+                        val & MDIO_PMA_STAT2_10GBER);
+
+       if (val & MDIO_PMA_STAT2_EXTABLE) {
+               val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE);
+               if (val < 0)
+                       return val;
+
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
+                                phydev->supported,
+                                val & MDIO_PMA_EXTABLE_10GBLRM);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+                                phydev->supported,
+                                val & MDIO_PMA_EXTABLE_10GBT);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+                                phydev->supported,
+                                val & MDIO_PMA_EXTABLE_10GBKX4);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+                                phydev->supported,
+                                val & MDIO_PMA_EXTABLE_10GBKR);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                                phydev->supported,
+                                val & MDIO_PMA_EXTABLE_1000BT);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+                                phydev->supported,
+                                val & MDIO_PMA_EXTABLE_1000BKX);
+
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+                                phydev->supported,
+                                val & MDIO_PMA_EXTABLE_100BTX);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+                                phydev->supported,
+                                val & MDIO_PMA_EXTABLE_100BTX);
+
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+                                phydev->supported,
+                                val & MDIO_PMA_EXTABLE_10BT);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+                                phydev->supported,
+                                val & MDIO_PMA_EXTABLE_10BT);
+
+               if (val & MDIO_PMA_EXTABLE_NBT) {
+                       val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+                                          MDIO_PMA_NG_EXTABLE);
+                       if (val < 0)
+                               return val;
+
+                       linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+                                        phydev->supported,
+                                        val & MDIO_PMA_NG_EXTABLE_2_5GBT);
+
+                       linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+                                        phydev->supported,
+                                        val & MDIO_PMA_NG_EXTABLE_5GBT);
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(genphy_c45_pma_read_abilities);
+
 /* The gen10g_* functions are the old Clause 45 stub */
 
 int gen10g_config_aneg(struct phy_device *phydev)
@@ -277,21 +436,11 @@ EXPORT_SYMBOL_GPL(gen10g_config_aneg);
 
 int gen10g_read_status(struct phy_device *phydev)
 {
-       u32 mmd_mask = phydev->c45_ids.devices_in_package;
-       int ret;
-
        /* For now just lie and say it's 10G all the time */
        phydev->speed = SPEED_10000;
        phydev->duplex = DUPLEX_FULL;
 
-       /* Avoid reading the vendor MMDs */
-       mmd_mask &= ~(BIT(MDIO_MMD_VEND1) | BIT(MDIO_MMD_VEND2));
-
-       ret = genphy_c45_read_link(phydev, mmd_mask);
-
-       phydev->link = ret > 0 ? 1 : 0;
-
-       return 0;
+       return genphy_c45_read_link(phydev);
 }
 EXPORT_SYMBOL_GPL(gen10g_read_status);
 
index 909b334..5016cd5 100644 (file)
@@ -4,6 +4,7 @@
  */
 #include <linux/export.h>
 #include <linux/phy.h>
+#include <linux/of.h>
 
 const char *phy_speed_to_str(int speed)
 {
@@ -338,6 +339,77 @@ size_t phy_speeds(unsigned int *speeds, size_t size,
        return count;
 }
 
+static int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
+{
+       const struct phy_setting *p;
+       int i;
+
+       for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
+               if (p->speed > max_speed)
+                       linkmode_clear_bit(p->bit, phydev->supported);
+               else
+                       break;
+       }
+
+       return 0;
+}
+
+int phy_set_max_speed(struct phy_device *phydev, u32 max_speed)
+{
+       int err;
+
+       err = __set_phy_supported(phydev, max_speed);
+       if (err)
+               return err;
+
+       linkmode_copy(phydev->advertising, phydev->supported);
+
+       return 0;
+}
+EXPORT_SYMBOL(phy_set_max_speed);
+
+void of_set_phy_supported(struct phy_device *phydev)
+{
+       struct device_node *node = phydev->mdio.dev.of_node;
+       u32 max_speed;
+
+       if (!IS_ENABLED(CONFIG_OF_MDIO))
+               return;
+
+       if (!node)
+               return;
+
+       if (!of_property_read_u32(node, "max-speed", &max_speed))
+               __set_phy_supported(phydev, max_speed);
+}
+
+void of_set_phy_eee_broken(struct phy_device *phydev)
+{
+       struct device_node *node = phydev->mdio.dev.of_node;
+       u32 broken = 0;
+
+       if (!IS_ENABLED(CONFIG_OF_MDIO))
+               return;
+
+       if (!node)
+               return;
+
+       if (of_property_read_bool(node, "eee-broken-100tx"))
+               broken |= MDIO_EEE_100TX;
+       if (of_property_read_bool(node, "eee-broken-1000t"))
+               broken |= MDIO_EEE_1000T;
+       if (of_property_read_bool(node, "eee-broken-10gt"))
+               broken |= MDIO_EEE_10GT;
+       if (of_property_read_bool(node, "eee-broken-1000kx"))
+               broken |= MDIO_EEE_1000KX;
+       if (of_property_read_bool(node, "eee-broken-10gkx4"))
+               broken |= MDIO_EEE_10GKX4;
+       if (of_property_read_bool(node, "eee-broken-10gkr"))
+               broken |= MDIO_EEE_10GKR;
+
+       phydev->eee_broken_modes = broken;
+}
+
 /**
  * phy_resolve_aneg_linkmode - resolve the advertisements into phy settings
  * @phydev: The phy_device struct
@@ -349,45 +421,16 @@ size_t phy_speeds(unsigned int *speeds, size_t size,
 void phy_resolve_aneg_linkmode(struct phy_device *phydev)
 {
        __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
+       int i;
 
        linkmode_and(common, phydev->lp_advertising, phydev->advertising);
 
-       if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, common)) {
-               phydev->speed = SPEED_10000;
-               phydev->duplex = DUPLEX_FULL;
-       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
-                                    common)) {
-               phydev->speed = SPEED_5000;
-               phydev->duplex = DUPLEX_FULL;
-       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
-                                    common)) {
-               phydev->speed = SPEED_2500;
-               phydev->duplex = DUPLEX_FULL;
-       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-                                    common)) {
-               phydev->speed = SPEED_1000;
-               phydev->duplex = DUPLEX_FULL;
-       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
-                                    common)) {
-               phydev->speed = SPEED_1000;
-               phydev->duplex = DUPLEX_HALF;
-       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
-                                    common)) {
-               phydev->speed = SPEED_100;
-               phydev->duplex = DUPLEX_FULL;
-       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
-                                    common)) {
-               phydev->speed = SPEED_100;
-               phydev->duplex = DUPLEX_HALF;
-       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
-                                    common)) {
-               phydev->speed = SPEED_10;
-               phydev->duplex = DUPLEX_FULL;
-       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
-                                    common)) {
-               phydev->speed = SPEED_10;
-               phydev->duplex = DUPLEX_HALF;
-       }
+       for (i = 0; i < ARRAY_SIZE(settings); i++)
+               if (test_bit(settings[i].bit, common)) {
+                       phydev->speed = settings[i].speed;
+                       phydev->duplex = settings[i].duplex;
+                       break;
+               }
 
        if (phydev->duplex == DUPLEX_FULL) {
                phydev->pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
@@ -414,15 +457,15 @@ static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad,
 }
 
 /**
- * phy_read_mmd - Convenience function for reading a register
+ * __phy_read_mmd - Convenience function for reading a register
  * from an MMD on a given PHY.
  * @phydev: The phy_device struct
  * @devad: The MMD to read from (0..31)
  * @regnum: The register on the MMD to read (0..65535)
  *
- * Same rules as for phy_read();
+ * Same rules as for __phy_read();
  */
-int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
+int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
 {
        int val;
 
@@ -434,33 +477,52 @@ int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
        } else if (phydev->is_c45) {
                u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff);
 
-               val = mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr);
+               val = __mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr);
        } else {
                struct mii_bus *bus = phydev->mdio.bus;
                int phy_addr = phydev->mdio.addr;
 
-               mutex_lock(&bus->mdio_lock);
                mmd_phy_indirect(bus, phy_addr, devad, regnum);
 
                /* Read the content of the MMD's selected register */
                val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
-               mutex_unlock(&bus->mdio_lock);
        }
        return val;
 }
+EXPORT_SYMBOL(__phy_read_mmd);
+
+/**
+ * phy_read_mmd - Convenience function for reading a register
+ * from an MMD on a given PHY.
+ * @phydev: The phy_device struct
+ * @devad: The MMD to read from
+ * @regnum: The register on the MMD to read
+ *
+ * Same rules as for phy_read();
+ */
+int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
+{
+       int ret;
+
+       mutex_lock(&phydev->mdio.bus->mdio_lock);
+       ret = __phy_read_mmd(phydev, devad, regnum);
+       mutex_unlock(&phydev->mdio.bus->mdio_lock);
+
+       return ret;
+}
 EXPORT_SYMBOL(phy_read_mmd);
 
 /**
- * phy_write_mmd - Convenience function for writing a register
+ * __phy_write_mmd - Convenience function for writing a register
  * on an MMD on a given PHY.
  * @phydev: The phy_device struct
  * @devad: The MMD to read from
  * @regnum: The register on the MMD to read
  * @val: value to write to @regnum
  *
- * Same rules as for phy_write();
+ * Same rules as for __phy_write();
  */
-int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
+int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
 {
        int ret;
 
@@ -472,27 +534,47 @@ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
        } else if (phydev->is_c45) {
                u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff);
 
-               ret = mdiobus_write(phydev->mdio.bus, phydev->mdio.addr,
-                                   addr, val);
+               ret = __mdiobus_write(phydev->mdio.bus, phydev->mdio.addr,
+                                     addr, val);
        } else {
                struct mii_bus *bus = phydev->mdio.bus;
                int phy_addr = phydev->mdio.addr;
 
-               mutex_lock(&bus->mdio_lock);
                mmd_phy_indirect(bus, phy_addr, devad, regnum);
 
                /* Write the data into MMD's selected register */
                __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
-               mutex_unlock(&bus->mdio_lock);
 
                ret = 0;
        }
        return ret;
 }
+EXPORT_SYMBOL(__phy_write_mmd);
+
+/**
+ * phy_write_mmd - Convenience function for writing a register
+ * on an MMD on a given PHY.
+ * @phydev: The phy_device struct
+ * @devad: The MMD to read from
+ * @regnum: The register on the MMD to read
+ * @val: value to write to @regnum
+ *
+ * Same rules as for phy_write();
+ */
+int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
+{
+       int ret;
+
+       mutex_lock(&phydev->mdio.bus->mdio_lock);
+       ret = __phy_write_mmd(phydev, devad, regnum, val);
+       mutex_unlock(&phydev->mdio.bus->mdio_lock);
+
+       return ret;
+}
 EXPORT_SYMBOL(phy_write_mmd);
 
 /**
- * __phy_modify() - Convenience function for modifying a PHY register
+ * __phy_modify_changed() - Convenience function for modifying a PHY register
  * @phydev: a pointer to a &struct phy_device
  * @regnum: register number
  * @mask: bit mask of bits to clear
@@ -500,16 +582,69 @@ EXPORT_SYMBOL(phy_write_mmd);
  *
  * Unlocked helper function which allows a PHY register to be modified as
  * new register value = (old register value & ~mask) | set
+ *
+ * Returns negative errno, 0 if there was no change, and 1 in case of change
  */
-int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
+int __phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask,
+                        u16 set)
 {
-       int ret;
+       int new, ret;
 
        ret = __phy_read(phydev, regnum);
        if (ret < 0)
                return ret;
 
-       ret = __phy_write(phydev, regnum, (ret & ~mask) | set);
+       new = (ret & ~mask) | set;
+       if (new == ret)
+               return 0;
+
+       ret = __phy_write(phydev, regnum, new);
+
+       return ret < 0 ? ret : 1;
+}
+EXPORT_SYMBOL_GPL(__phy_modify_changed);
+
+/**
+ * phy_modify_changed - Function for modifying a PHY register
+ * @phydev: the phy_device struct
+ * @regnum: register number to modify
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in mask to write to @regnum
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ *
+ * Returns negative errno, 0 if there was no change, and 1 in case of change
+ */
+int phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
+{
+       int ret;
+
+       mutex_lock(&phydev->mdio.bus->mdio_lock);
+       ret = __phy_modify_changed(phydev, regnum, mask, set);
+       mutex_unlock(&phydev->mdio.bus->mdio_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_modify_changed);
+
+/**
+ * __phy_modify - Convenience function for modifying a PHY register
+ * @phydev: the phy_device struct
+ * @regnum: register number to modify
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in mask to write to @regnum
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ */
+int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
+{
+       int ret;
+
+       ret = __phy_modify_changed(phydev, regnum, mask, set);
 
        return ret < 0 ? ret : 0;
 }
@@ -538,6 +673,113 @@ int phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
 }
 EXPORT_SYMBOL_GPL(phy_modify);
 
+/**
+ * __phy_modify_mmd_changed - Function for modifying a register on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in mask to write to @regnum
+ *
+ * Unlocked helper function which allows a MMD register to be modified as
+ * new register value = (old register value & ~mask) | set
+ *
+ * Returns negative errno, 0 if there was no change, and 1 in case of change
+ */
+int __phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
+                            u16 mask, u16 set)
+{
+       int new, ret;
+
+       ret = __phy_read_mmd(phydev, devad, regnum);
+       if (ret < 0)
+               return ret;
+
+       new = (ret & ~mask) | set;
+       if (new == ret)
+               return 0;
+
+       ret = __phy_write_mmd(phydev, devad, regnum, new);
+
+       return ret < 0 ? ret : 1;
+}
+EXPORT_SYMBOL_GPL(__phy_modify_mmd_changed);
+
+/**
+ * phy_modify_mmd_changed - Function for modifying a register on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in mask to write to @regnum
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ *
+ * Returns negative errno, 0 if there was no change, and 1 in case of change
+ */
+int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
+                          u16 mask, u16 set)
+{
+       int ret;
+
+       mutex_lock(&phydev->mdio.bus->mdio_lock);
+       ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
+       mutex_unlock(&phydev->mdio.bus->mdio_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_modify_mmd_changed);
+
+/**
+ * __phy_modify_mmd - Convenience function for modifying a register on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in mask to write to @regnum
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ */
+int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
+                    u16 mask, u16 set)
+{
+       int ret;
+
+       ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
+
+       return ret < 0 ? ret : 0;
+}
+EXPORT_SYMBOL_GPL(__phy_modify_mmd);
+
+/**
+ * phy_modify_mmd - Convenience function for modifying a register on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in mask to write to @regnum
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ */
+int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
+                  u16 mask, u16 set)
+{
+       int ret;
+
+       mutex_lock(&phydev->mdio.bus->mdio_lock);
+       ret = __phy_modify_mmd(phydev, devad, regnum, mask, set);
+       mutex_unlock(&phydev->mdio.bus->mdio_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_modify_mmd);
+
 static int __phy_read_page(struct phy_device *phydev)
 {
        return phydev->drv->read_page(phydev);
index d12aa51..69dc64a 100644 (file)
@@ -543,7 +543,7 @@ int phy_start_aneg(struct phy_device *phydev)
        if (err < 0)
                goto out_unlock;
 
-       if (__phy_is_started(phydev)) {
+       if (phy_is_started(phydev)) {
                if (phydev->autoneg == AUTONEG_ENABLE) {
                        err = phy_check_link_status(phydev);
                } else {
@@ -699,7 +699,7 @@ void phy_stop_machine(struct phy_device *phydev)
        cancel_delayed_work_sync(&phydev->state_queue);
 
        mutex_lock(&phydev->lock);
-       if (__phy_is_started(phydev))
+       if (phy_is_started(phydev))
                phydev->state = PHY_UP;
        mutex_unlock(&phydev->lock);
 }
@@ -752,9 +752,6 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
 {
        struct phy_device *phydev = phy_dat;
 
-       if (!phy_is_started(phydev))
-               return IRQ_NONE;                /* It can't be ours.  */
-
        if (phydev->drv->did_interrupt && !phydev->drv->did_interrupt(phydev))
                return IRQ_NONE;
 
@@ -813,15 +810,14 @@ EXPORT_SYMBOL(phy_request_interrupt);
  */
 void phy_stop(struct phy_device *phydev)
 {
-       mutex_lock(&phydev->lock);
-
-       if (!__phy_is_started(phydev)) {
+       if (!phy_is_started(phydev)) {
                WARN(1, "called from state %s\n",
                     phy_state_to_str(phydev->state));
-               mutex_unlock(&phydev->lock);
                return;
        }
 
+       mutex_lock(&phydev->lock);
+
        if (phy_interrupt_is_valid(phydev))
                phy_disable_interrupts(phydev);
 
@@ -961,8 +957,10 @@ void phy_state_machine(struct work_struct *work)
         * state machine would be pointless and possibly error prone when
         * called from phy_disconnect() synchronously.
         */
+       mutex_lock(&phydev->lock);
        if (phy_polling_mode(phydev) && phy_is_started(phydev))
                phy_queue_state_machine(phydev, PHY_STATE_TIME);
+       mutex_unlock(&phydev->lock);
 }
 
 /**
@@ -1060,17 +1058,12 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
                if (!phy_check_valid(phydev->speed, phydev->duplex, common))
                        goto eee_exit_err;
 
-               if (clk_stop_enable) {
+               if (clk_stop_enable)
                        /* Configure the PHY to stop receiving xMII
                         * clock while it is signaling LPI.
                         */
-                       int val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
-                       if (val < 0)
-                               return val;
-
-                       val |= MDIO_PCS_CTRL1_CLKSTOP_EN;
-                       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, val);
-               }
+                       phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1,
+                                        MDIO_PCS_CTRL1_CLKSTOP_EN);
 
                return 0; /* EEE supported */
        }
index 891e017..7e71124 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/mdio.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
-#include <linux/of.h>
 
 MODULE_DESCRIPTION("PHY library");
 MODULE_AUTHOR("Andy Fleming");
@@ -676,13 +675,16 @@ static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
        phy_reg = mdiobus_read(bus, addr, reg_addr);
        if (phy_reg < 0)
                return -EIO;
-       *devices_in_package = (phy_reg & 0xffff) << 16;
+       *devices_in_package = phy_reg << 16;
 
        reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS1;
        phy_reg = mdiobus_read(bus, addr, reg_addr);
        if (phy_reg < 0)
                return -EIO;
-       *devices_in_package |= (phy_reg & 0xffff);
+       *devices_in_package |= phy_reg;
+
+       /* Bit 0 doesn't represent a device, it indicates c22 regs presence */
+       *devices_in_package &= ~BIT(0);
 
        return 0;
 }
@@ -743,13 +745,13 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id,
                phy_reg = mdiobus_read(bus, addr, reg_addr);
                if (phy_reg < 0)
                        return -EIO;
-               c45_ids->device_ids[i] = (phy_reg & 0xffff) << 16;
+               c45_ids->device_ids[i] = phy_reg << 16;
 
                reg_addr = MII_ADDR_C45 | i << 16 | MII_PHYSID2;
                phy_reg = mdiobus_read(bus, addr, reg_addr);
                if (phy_reg < 0)
                        return -EIO;
-               c45_ids->device_ids[i] |= (phy_reg & 0xffff);
+               c45_ids->device_ids[i] |= phy_reg;
        }
        *phy_id = 0;
        return 0;
@@ -786,14 +788,14 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id,
                return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO;
        }
 
-       *phy_id = (phy_reg & 0xffff) << 16;
+       *phy_id = phy_reg << 16;
 
        /* Grab the bits from PHYIR2, and put them in the lower half */
        phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
        if (phy_reg < 0)
                return -EIO;
 
-       *phy_id |= (phy_reg & 0xffff);
+       *phy_id |= phy_reg;
 
        return 0;
 }
@@ -1513,7 +1515,7 @@ EXPORT_SYMBOL(phy_reset_after_clk_enable);
 static int genphy_config_advert(struct phy_device *phydev)
 {
        u32 advertise;
-       int oldadv, adv, bmsr;
+       int bmsr, adv;
        int err, changed = 0;
 
        /* Only allow advertising what this PHY supports */
@@ -1526,22 +1528,14 @@ static int genphy_config_advert(struct phy_device *phydev)
                            phydev->advertising);
 
        /* Setup standard advertisement */
-       adv = phy_read(phydev, MII_ADVERTISE);
-       if (adv < 0)
-               return adv;
-
-       oldadv = adv;
-       adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
-                ADVERTISE_PAUSE_ASYM);
-       adv |= ethtool_adv_to_mii_adv_t(advertise);
-
-       if (adv != oldadv) {
-               err = phy_write(phydev, MII_ADVERTISE, adv);
-
-               if (err < 0)
-                       return err;
+       err = phy_modify_changed(phydev, MII_ADVERTISE,
+                                ADVERTISE_ALL | ADVERTISE_100BASE4 |
+                                ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
+                                ethtool_adv_to_mii_adv_t(advertise));
+       if (err < 0)
+               return err;
+       if (err > 0)
                changed = 1;
-       }
 
        bmsr = phy_read(phydev, MII_BMSR);
        if (bmsr < 0)
@@ -1555,25 +1549,20 @@ static int genphy_config_advert(struct phy_device *phydev)
                return changed;
 
        /* Configure gigabit if it's supported */
-       adv = phy_read(phydev, MII_CTRL1000);
-       if (adv < 0)
-               return adv;
-
-       oldadv = adv;
-       adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
-
+       adv = 0;
        if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
                              phydev->supported) ||
            linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
                              phydev->supported))
-               adv |= ethtool_adv_to_mii_ctrl1000_t(advertise);
+               adv = ethtool_adv_to_mii_ctrl1000_t(advertise);
 
-       if (adv != oldadv)
-               changed = 1;
-
-       err = phy_write(phydev, MII_CTRL1000, adv);
+       err = phy_modify_changed(phydev, MII_CTRL1000,
+                                ADVERTISE_1000FULL | ADVERTISE_1000HALF,
+                                adv);
        if (err < 0)
                return err;
+       if (err > 0)
+               changed = 1;
 
        return changed;
 }
@@ -1588,31 +1577,16 @@ static int genphy_config_advert(struct phy_device *phydev)
  */
 static int genphy_config_eee_advert(struct phy_device *phydev)
 {
-       int broken = phydev->eee_broken_modes;
-       int old_adv, adv;
+       int err;
 
        /* Nothing to disable */
-       if (!broken)
-               return 0;
-
-       /* If the following call fails, we assume that EEE is not
-        * supported by the phy. If we read 0, EEE is not advertised
-        * In both case, we don't need to continue
-        */
-       adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
-       if (adv <= 0)
-               return 0;
-
-       old_adv = adv;
-       adv &= ~broken;
-
-       /* Advertising remains unchanged with the broken mask */
-       if (old_adv == adv)
+       if (!phydev->eee_broken_modes)
                return 0;
 
-       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv);
-
-       return 1;
+       err = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV,
+                                    phydev->eee_broken_modes, 0);
+       /* If the call failed, we assume that EEE is not supported */
+       return err < 0 ? 0 : err;
 }
 
 /**
@@ -1729,10 +1703,19 @@ int genphy_update_link(struct phy_device *phydev)
 {
        int status;
 
-       /* Do a fake read */
-       status = phy_read(phydev, MII_BMSR);
-       if (status < 0)
-               return status;
+       /* The link state is latched low so that momentary link
+        * drops can be detected. Do not double-read the status
+        * in polling mode to detect such short link drops.
+        */
+       if (!phy_polling_mode(phydev)) {
+               status = phy_read(phydev, MII_BMSR);
+               if (status < 0) {
+                       return status;
+               } else if (status & BMSR_LSTATUS) {
+                       phydev->link = 1;
+                       return 0;
+               }
+       }
 
        /* Read link and autonegotiation status */
        status = phy_read(phydev, MII_BMSR);
@@ -1763,8 +1746,6 @@ int genphy_read_status(struct phy_device *phydev)
        int err;
        int lpa;
        int lpagb = 0;
-       int common_adv;
-       int common_adv_gb = 0;
 
        /* Update the link, but return if there was an error */
        err = genphy_update_link(phydev);
@@ -1796,7 +1777,6 @@ int genphy_read_status(struct phy_device *phydev)
 
                        mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
                                                        lpagb);
-                       common_adv_gb = lpagb & adv << 2;
                }
 
                lpa = phy_read(phydev, MII_LPA);
@@ -1809,31 +1789,12 @@ int genphy_read_status(struct phy_device *phydev)
                if (adv < 0)
                        return adv;
 
-               common_adv = lpa & adv;
-
                phydev->speed = SPEED_10;
                phydev->duplex = DUPLEX_HALF;
                phydev->pause = 0;
                phydev->asym_pause = 0;
 
-               if (common_adv_gb & (LPA_1000FULL | LPA_1000HALF)) {
-                       phydev->speed = SPEED_1000;
-
-                       if (common_adv_gb & LPA_1000FULL)
-                               phydev->duplex = DUPLEX_FULL;
-               } else if (common_adv & (LPA_100FULL | LPA_100HALF)) {
-                       phydev->speed = SPEED_100;
-
-                       if (common_adv & LPA_100FULL)
-                               phydev->duplex = DUPLEX_FULL;
-               } else
-                       if (common_adv & LPA_10FULL)
-                               phydev->duplex = DUPLEX_FULL;
-
-               if (phydev->duplex == DUPLEX_FULL) {
-                       phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
-                       phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
-               }
+               phy_resolve_aneg_linkmode(phydev);
        } else {
                int bmcr = phy_read(phydev, MII_BMCR);
 
@@ -1965,44 +1926,6 @@ int genphy_loopback(struct phy_device *phydev, bool enable)
 }
 EXPORT_SYMBOL(genphy_loopback);
 
-static int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
-{
-       switch (max_speed) {
-       case SPEED_10:
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
-                                  phydev->supported);
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
-                                  phydev->supported);
-               /* fall through */
-       case SPEED_100:
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
-                                  phydev->supported);
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-                                  phydev->supported);
-               break;
-       case SPEED_1000:
-               break;
-       default:
-               return -ENOTSUPP;
-       }
-
-       return 0;
-}
-
-int phy_set_max_speed(struct phy_device *phydev, u32 max_speed)
-{
-       int err;
-
-       err = __set_phy_supported(phydev, max_speed);
-       if (err)
-               return err;
-
-       linkmode_copy(phydev->advertising, phydev->supported);
-
-       return 0;
-}
-EXPORT_SYMBOL(phy_set_max_speed);
-
 /**
  * phy_remove_link_mode - Remove a supported link mode
  * @phydev: phy_device structure to remove link mode from
@@ -2133,48 +2056,6 @@ bool phy_validate_pause(struct phy_device *phydev,
 }
 EXPORT_SYMBOL(phy_validate_pause);
 
-static void of_set_phy_supported(struct phy_device *phydev)
-{
-       struct device_node *node = phydev->mdio.dev.of_node;
-       u32 max_speed;
-
-       if (!IS_ENABLED(CONFIG_OF_MDIO))
-               return;
-
-       if (!node)
-               return;
-
-       if (!of_property_read_u32(node, "max-speed", &max_speed))
-               __set_phy_supported(phydev, max_speed);
-}
-
-static void of_set_phy_eee_broken(struct phy_device *phydev)
-{
-       struct device_node *node = phydev->mdio.dev.of_node;
-       u32 broken = 0;
-
-       if (!IS_ENABLED(CONFIG_OF_MDIO))
-               return;
-
-       if (!node)
-               return;
-
-       if (of_property_read_bool(node, "eee-broken-100tx"))
-               broken |= MDIO_EEE_100TX;
-       if (of_property_read_bool(node, "eee-broken-1000t"))
-               broken |= MDIO_EEE_1000T;
-       if (of_property_read_bool(node, "eee-broken-10gt"))
-               broken |= MDIO_EEE_10GT;
-       if (of_property_read_bool(node, "eee-broken-1000kx"))
-               broken |= MDIO_EEE_1000KX;
-       if (of_property_read_bool(node, "eee-broken-10gkx4"))
-               broken |= MDIO_EEE_10GKX4;
-       if (of_property_read_bool(node, "eee-broken-10gkr"))
-               broken |= MDIO_EEE_10GKR;
-
-       phydev->eee_broken_modes = broken;
-}
-
 static bool phy_drv_supports_irq(struct phy_driver *phydrv)
 {
        return phydrv->config_intr && phydrv->ack_interrupt;
@@ -2208,11 +2089,30 @@ static int phy_probe(struct device *dev)
 
        mutex_lock(&phydev->lock);
 
+       if (phydev->drv->probe) {
+               /* Deassert the reset signal */
+               phy_device_reset(phydev, 0);
+
+               err = phydev->drv->probe(phydev);
+               if (err) {
+                       /* Assert the reset signal */
+                       phy_device_reset(phydev, 1);
+                       goto out;
+               }
+       }
+
        /* Start out supporting everything. Eventually,
         * a controller will attach, and may modify one
         * or both of these values
         */
-       linkmode_copy(phydev->supported, phydrv->features);
+       if (phydrv->features) {
+               linkmode_copy(phydev->supported, phydrv->features);
+       } else {
+               err = phydrv->get_features(phydev);
+               if (err)
+                       goto out;
+       }
+
        of_set_phy_supported(phydev);
        linkmode_copy(phydev->advertising, phydev->supported);
 
@@ -2232,20 +2132,8 @@ static int phy_probe(struct device *dev)
         * (e.g. hardware erratum) where the driver wants to set only one
         * of these bits.
         */
-       if (test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydrv->features) ||
-           test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydrv->features)) {
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
-                                  phydev->supported);
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
-                                  phydev->supported);
-               if (test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydrv->features))
-                       linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
-                                        phydev->supported);
-               if (test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
-                            phydrv->features))
-                       linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
-                                        phydev->supported);
-       } else {
+       if (!test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported) &&
+           !test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported)) {
                linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
                                 phydev->supported);
                linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
@@ -2255,17 +2143,7 @@ static int phy_probe(struct device *dev)
        /* Set the state to READY by default */
        phydev->state = PHY_READY;
 
-       if (phydev->drv->probe) {
-               /* Deassert the reset signal */
-               phy_device_reset(phydev, 0);
-
-               err = phydev->drv->probe(phydev);
-               if (err) {
-                       /* Assert the reset signal */
-                       phy_device_reset(phydev, 1);
-               }
-       }
-
+out:
        mutex_unlock(&phydev->lock);
 
        return err;
@@ -2301,7 +2179,11 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
 {
        int retval;
 
-       if (WARN_ON(!new_driver->features)) {
+       /* Either the features are hard coded, or dynamically
+        * determine. It cannot be both or neither
+        */
+       if (WARN_ON((!new_driver->features && !new_driver->get_features) ||
+                   (new_driver->features && new_driver->get_features))) {
                pr_err("%s: Driver features are missing\n", new_driver->name);
                return -EINVAL;
        }
index 2e21ce4..59d175a 100644 (file)
@@ -302,6 +302,13 @@ static void phylink_mac_config(struct phylink *pl,
        pl->ops->mac_config(pl->netdev, pl->link_an_mode, state);
 }
 
+static void phylink_mac_config_up(struct phylink *pl,
+                                 const struct phylink_link_state *state)
+{
+       if (state->link)
+               phylink_mac_config(pl, state);
+}
+
 static void phylink_mac_an_restart(struct phylink *pl)
 {
        if (pl->link_config.an_enabled &&
@@ -401,12 +408,12 @@ static void phylink_resolve(struct work_struct *w)
                case MLO_AN_PHY:
                        link_state = pl->phy_state;
                        phylink_resolve_flow(pl, &link_state);
-                       phylink_mac_config(pl, &link_state);
+                       phylink_mac_config_up(pl, &link_state);
                        break;
 
                case MLO_AN_FIXED:
                        phylink_get_fixed_state(pl, &link_state);
-                       phylink_mac_config(pl, &link_state);
+                       phylink_mac_config_up(pl, &link_state);
                        break;
 
                case MLO_AN_INBAND:
@@ -471,6 +478,17 @@ static void phylink_run_resolve(struct phylink *pl)
                queue_work(system_power_efficient_wq, &pl->resolve);
 }
 
+static void phylink_run_resolve_and_disable(struct phylink *pl, int bit)
+{
+       unsigned long state = pl->phylink_disable_state;
+
+       set_bit(bit, &pl->phylink_disable_state);
+       if (state == 0) {
+               queue_work(system_power_efficient_wq, &pl->resolve);
+               flush_work(&pl->resolve);
+       }
+}
+
 static void phylink_fixed_poll(struct timer_list *t)
 {
        struct phylink *pl = container_of(t, struct phylink, link_poll);
@@ -920,9 +938,7 @@ void phylink_stop(struct phylink *pl)
        if (pl->link_an_mode == MLO_AN_FIXED && !IS_ERR(pl->link_gpio))
                del_timer_sync(&pl->link_poll);
 
-       set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
-       queue_work(system_power_efficient_wq, &pl->resolve);
-       flush_work(&pl->resolve);
+       phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED);
 }
 EXPORT_SYMBOL_GPL(phylink_stop);
 
@@ -1265,6 +1281,24 @@ int phylink_get_eee_err(struct phylink *pl)
 EXPORT_SYMBOL_GPL(phylink_get_eee_err);
 
 /**
+ * phylink_init_eee() - init and check the EEE features
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ * @clk_stop_enable: allow PHY to stop receive clock
+ *
+ * Must be called either with RTNL held or within mac_link_up()
+ */
+int phylink_init_eee(struct phylink *pl, bool clk_stop_enable)
+{
+       int ret = -EOPNOTSUPP;
+
+       if (pl->phydev)
+               ret = phy_init_eee(pl->phydev, clk_stop_enable);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_init_eee);
+
+/**
  * phylink_ethtool_get_eee() - read the energy efficient ethernet parameters
  * @pl: a pointer to a &struct phylink returned from phylink_create()
  * @eee: a pointer to a &struct ethtool_eee for the read parameters
@@ -1628,9 +1662,7 @@ static void phylink_sfp_link_down(void *upstream)
 
        ASSERT_RTNL();
 
-       set_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state);
-       queue_work(system_power_efficient_wq, &pl->resolve);
-       flush_work(&pl->resolve);
+       phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_LINK);
 }
 
 static void phylink_sfp_link_up(void *upstream)
index 75543f9..d8c4b9a 100644 (file)
@@ -273,6 +273,15 @@ static struct phy_driver realtek_drvs[] = {
                .read_page      = rtl821x_read_page,
                .write_page     = rtl821x_write_page,
        }, {
+               PHY_ID_MATCH_EXACT(0x001cc800),
+               .name           = "Generic Realtek PHY",
+               .features       = PHY_GBIT_FEATURES,
+               .config_init    = genphy_config_init,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
+               .read_page      = rtl821x_read_page,
+               .write_page     = rtl821x_write_page,
+       }, {
                PHY_ID_MATCH_EXACT(0x001cc961),
                .name           = "RTL8366RB Gigabit Ethernet",
                .features       = PHY_GBIT_FEATURES,
index ad9db65..fef701b 100644 (file)
@@ -347,6 +347,7 @@ static int sfp_register_bus(struct sfp_bus *bus)
                                return ret;
                }
        }
+       bus->socket_ops->attach(bus->sfp);
        if (bus->started)
                bus->socket_ops->start(bus->sfp);
        bus->netdev->sfp_bus = bus;
@@ -362,6 +363,7 @@ static void sfp_unregister_bus(struct sfp_bus *bus)
        if (bus->registered) {
                if (bus->started)
                        bus->socket_ops->stop(bus->sfp);
+               bus->socket_ops->detach(bus->sfp);
                if (bus->phydev && ops && ops->disconnect_phy)
                        ops->disconnect_phy(bus->upstream);
        }
index 298ab75..d4635c2 100644 (file)
@@ -185,6 +185,7 @@ struct sfp {
 
        struct gpio_desc *gpio[GPIO_MAX];
 
+       bool attached;
        unsigned int state;
        struct delayed_work poll;
        struct delayed_work timeout;
@@ -1476,7 +1477,7 @@ static void sfp_sm_event(struct sfp *sfp, unsigned int event)
         */
        switch (sfp->sm_mod_state) {
        default:
-               if (event == SFP_E_INSERT) {
+               if (event == SFP_E_INSERT && sfp->attached) {
                        sfp_module_tx_disable(sfp);
                        sfp_sm_ins_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT);
                }
@@ -1608,6 +1609,19 @@ static void sfp_sm_event(struct sfp *sfp, unsigned int event)
        mutex_unlock(&sfp->sm_mutex);
 }
 
+static void sfp_attach(struct sfp *sfp)
+{
+       sfp->attached = true;
+       if (sfp->state & SFP_F_PRESENT)
+               sfp_sm_event(sfp, SFP_E_INSERT);
+}
+
+static void sfp_detach(struct sfp *sfp)
+{
+       sfp->attached = false;
+       sfp_sm_event(sfp, SFP_E_REMOVE);
+}
+
 static void sfp_start(struct sfp *sfp)
 {
        sfp_sm_event(sfp, SFP_E_DEV_UP);
@@ -1668,6 +1682,8 @@ static int sfp_module_eeprom(struct sfp *sfp, struct ethtool_eeprom *ee,
 }
 
 static const struct sfp_socket_ops sfp_module_ops = {
+       .attach = sfp_attach,
+       .detach = sfp_detach,
        .start = sfp_start,
        .stop = sfp_stop,
        .module_info = sfp_module_info,
@@ -1835,10 +1851,6 @@ static int sfp_probe(struct platform_device *pdev)
        dev_info(sfp->dev, "Host maximum power %u.%uW\n",
                 sfp->max_power_mW / 1000, (sfp->max_power_mW / 100) % 10);
 
-       sfp->sfp_bus = sfp_register_socket(sfp->dev, sfp, &sfp_module_ops);
-       if (!sfp->sfp_bus)
-               return -ENOMEM;
-
        /* Get the initial state, and always signal TX disable,
         * since the network interface will not be up.
         */
@@ -1849,10 +1861,6 @@ static int sfp_probe(struct platform_device *pdev)
                sfp->state |= SFP_F_RATE_SELECT;
        sfp_set_state(sfp, sfp->state);
        sfp_module_tx_disable(sfp);
-       rtnl_lock();
-       if (sfp->state & SFP_F_PRESENT)
-               sfp_sm_event(sfp, SFP_E_INSERT);
-       rtnl_unlock();
 
        for (i = 0; i < GPIO_MAX; i++) {
                if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
@@ -1885,6 +1893,10 @@ static int sfp_probe(struct platform_device *pdev)
                dev_warn(sfp->dev,
                         "No tx_disable pin: SFP modules will always be emitting.\n");
 
+       sfp->sfp_bus = sfp_register_socket(sfp->dev, sfp, &sfp_module_ops);
+       if (!sfp->sfp_bus)
+               return -ENOMEM;
+
        return 0;
 }
 
index 31b0acf..64f54b0 100644 (file)
@@ -7,6 +7,8 @@
 struct sfp;
 
 struct sfp_socket_ops {
+       void (*attach)(struct sfp *sfp);
+       void (*detach)(struct sfp *sfp);
        void (*start)(struct sfp *sfp);
        void (*stop)(struct sfp *sfp);
        int (*module_info)(struct sfp *sfp, struct ethtool_modinfo *modinfo);
index afd9d25..958f1cf 100644 (file)
@@ -256,17 +256,6 @@ static void __team_option_inst_mark_removed_port(struct team *team,
        }
 }
 
-static bool __team_option_inst_tmp_find(const struct list_head *opts,
-                                       const struct team_option_inst *needle)
-{
-       struct team_option_inst *opt_inst;
-
-       list_for_each_entry(opt_inst, opts, tmp_list)
-               if (opt_inst == needle)
-                       return true;
-       return false;
-}
-
 static int __team_options_register(struct team *team,
                                   const struct team_option *option,
                                   size_t option_count)
@@ -2460,7 +2449,6 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
        int err = 0;
        int i;
        struct nlattr *nl_option;
-       LIST_HEAD(opt_inst_list);
 
        rtnl_lock();
 
@@ -2480,6 +2468,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
                struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1];
                struct nlattr *attr;
                struct nlattr *attr_data;
+               LIST_HEAD(opt_inst_list);
                enum team_option_type opt_type;
                int opt_port_ifindex = 0; /* != 0 for per-port options */
                u32 opt_array_index = 0;
@@ -2584,23 +2573,17 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
                        if (err)
                                goto team_put;
                        opt_inst->changed = true;
-
-                       /* dumb/evil user-space can send us duplicate opt,
-                        * keep only the last one
-                        */
-                       if (__team_option_inst_tmp_find(&opt_inst_list,
-                                                       opt_inst))
-                               continue;
-
                        list_add(&opt_inst->tmp_list, &opt_inst_list);
                }
                if (!opt_found) {
                        err = -ENOENT;
                        goto team_put;
                }
-       }
 
-       err = team_nl_send_event_options_get(team, &opt_inst_list);
+               err = team_nl_send_event_options_get(team, &opt_inst_list);
+               if (err)
+                       break;
+       }
 
 team_put:
        team_nl_team_put(team);
index 18656c4..fed298c 100644 (file)
@@ -866,8 +866,6 @@ static int tun_attach(struct tun_struct *tun, struct file *file,
        if (rtnl_dereference(tun->xdp_prog))
                sock_set_flag(&tfile->sk, SOCK_XDP);
 
-       tun_set_real_num_queues(tun);
-
        /* device is allowed to go away first, so no need to hold extra
         * refcnt.
         */
@@ -879,6 +877,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file,
        rcu_assign_pointer(tfile->tun, tun);
        rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile);
        tun->numqueues++;
+       tun_set_real_num_queues(tun);
 out:
        return err;
 }
index 78b16eb..63aaae4 100644 (file)
@@ -361,8 +361,8 @@ static int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *i
        else
                return -EINVAL;
 
-       dev = alloc_netdev(sizeof(*pnd) + sizeof(pnd->urbs[0]) * rxq_size,
-                          ifname, NET_NAME_UNKNOWN, usbpn_setup);
+       dev = alloc_netdev(struct_size(pnd, urbs, rxq_size), ifname,
+                          NET_NAME_UNKNOWN, usbpn_setup);
        if (!dev)
                return -ENOMEM;
 
index e96bc0c..3d92ea6 100644 (file)
@@ -2051,8 +2051,7 @@ static struct phy_device *lan7801_phy_init(struct lan78xx_net *dev)
        phydev = phy_find_first(dev->mdiobus);
        if (!phydev) {
                netdev_dbg(dev->net, "PHY Not Found!! Registering Fixed PHY\n");
-               phydev = fixed_phy_register(PHY_POLL, &fphy_status, -1,
-                                           NULL);
+               phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
                if (IS_ERR(phydev)) {
                        netdev_err(dev->net, "No PHY/fixed_PHY found\n");
                        return NULL;
index f4247b2..63e44e7 100644 (file)
@@ -1011,6 +1011,7 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
        switch (cmd) {
        case SIOCDEVPRIVATE:
                data[0] = pegasus->phy;
+               /* fall through */
        case SIOCDEVPRIVATE + 1:
                read_mii_word(pegasus, data[0], data[1] & 0x1f, &data[3]);
                res = 0;
index 3f145e4..59dbdbb 100644 (file)
@@ -847,6 +847,7 @@ static int rtl8150_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
        switch (cmd) {
        case SIOCDEVPRIVATE:
                data[0] = dev->phy;
+               /* fall through */
        case SIOCDEVPRIVATE + 1:
                read_mii_word(dev, dev->phy, (data[1] & 0x1f), &data[3]);
                break;
index f412ea1..fbf890e 100644 (file)
@@ -540,8 +540,10 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_rq *rq,
                        goto xdp_xmit;
                default:
                        bpf_warn_invalid_xdp_action(act);
+                       /* fall through */
                case XDP_ABORTED:
                        trace_xdp_exception(rq->dev, xdp_prog, act);
+                       /* fall through */
                case XDP_DROP:
                        goto err_xdp;
                }
@@ -661,8 +663,10 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, struct sk_buff *skb,
                goto xdp_xmit;
        default:
                bpf_warn_invalid_xdp_action(act);
+               /* fall through */
        case XDP_ABORTED:
                trace_xdp_exception(rq->dev, xdp_prog, act);
+               /* fall through */
        case XDP_DROP:
                goto drop;
        }
index 2a0edd4..7eb38ea 100644 (file)
@@ -57,6 +57,8 @@ module_param(napi_tx, bool, 0644);
 #define VIRTIO_XDP_TX          BIT(0)
 #define VIRTIO_XDP_REDIR       BIT(1)
 
+#define VIRTIO_XDP_FLAG        BIT(0)
+
 /* RX packet size EWMA. The average packet size is used to determine the packet
  * buffer size when refilling RX rings. As the entire RX ring may be refilled
  * at once, the weight is chosen so that the EWMA will be insensitive to short-
@@ -252,6 +254,21 @@ struct padded_vnet_hdr {
        char padding[4];
 };
 
+static bool is_xdp_frame(void *ptr)
+{
+       return (unsigned long)ptr & VIRTIO_XDP_FLAG;
+}
+
+static void *xdp_to_ptr(struct xdp_frame *ptr)
+{
+       return (void *)((unsigned long)ptr | VIRTIO_XDP_FLAG);
+}
+
+static struct xdp_frame *ptr_to_xdp(void *ptr)
+{
+       return (struct xdp_frame *)((unsigned long)ptr & ~VIRTIO_XDP_FLAG);
+}
+
 /* Converting between virtqueue no. and kernel tx/rx queue no.
  * 0:rx0 1:tx0 2:rx1 3:tx1 ... 2N:rxN 2N+1:txN 2N+2:cvq
  */
@@ -462,7 +479,8 @@ static int __virtnet_xdp_xmit_one(struct virtnet_info *vi,
 
        sg_init_one(sq->sg, xdpf->data, xdpf->len);
 
-       err = virtqueue_add_outbuf(sq->vq, sq->sg, 1, xdpf, GFP_ATOMIC);
+       err = virtqueue_add_outbuf(sq->vq, sq->sg, 1, xdp_to_ptr(xdpf),
+                                  GFP_ATOMIC);
        if (unlikely(err))
                return -ENOSPC; /* Caller handle free/refcnt */
 
@@ -482,36 +500,47 @@ static int virtnet_xdp_xmit(struct net_device *dev,
 {
        struct virtnet_info *vi = netdev_priv(dev);
        struct receive_queue *rq = vi->rq;
-       struct xdp_frame *xdpf_sent;
        struct bpf_prog *xdp_prog;
        struct send_queue *sq;
        unsigned int len;
+       int packets = 0;
+       int bytes = 0;
        int drops = 0;
        int kicks = 0;
        int ret, err;
+       void *ptr;
        int i;
 
-       sq = virtnet_xdp_sq(vi);
-
-       if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) {
-               ret = -EINVAL;
-               drops = n;
-               goto out;
-       }
-
        /* Only allow ndo_xdp_xmit if XDP is loaded on dev, as this
         * indicate XDP resources have been successfully allocated.
         */
        xdp_prog = rcu_dereference(rq->xdp_prog);
-       if (!xdp_prog) {
-               ret = -ENXIO;
+       if (!xdp_prog)
+               return -ENXIO;
+
+       sq = virtnet_xdp_sq(vi);
+
+       if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) {
+               ret = -EINVAL;
                drops = n;
                goto out;
        }
 
        /* Free up any pending old buffers before queueing new ones. */
-       while ((xdpf_sent = virtqueue_get_buf(sq->vq, &len)) != NULL)
-               xdp_return_frame(xdpf_sent);
+       while ((ptr = virtqueue_get_buf(sq->vq, &len)) != NULL) {
+               if (likely(is_xdp_frame(ptr))) {
+                       struct xdp_frame *frame = ptr_to_xdp(ptr);
+
+                       bytes += frame->len;
+                       xdp_return_frame(frame);
+               } else {
+                       struct sk_buff *skb = ptr;
+
+                       bytes += skb->len;
+                       napi_consume_skb(skb, false);
+               }
+               packets++;
+       }
 
        for (i = 0; i < n; i++) {
                struct xdp_frame *xdpf = frames[i];
@@ -530,6 +559,8 @@ static int virtnet_xdp_xmit(struct net_device *dev,
        }
 out:
        u64_stats_update_begin(&sq->stats.syncp);
+       sq->stats.bytes += bytes;
+       sq->stats.packets += packets;
        sq->stats.xdp_tx += n;
        sq->stats.xdp_tx_drops += drops;
        sq->stats.kicks += kicks;
@@ -1333,18 +1364,26 @@ static int virtnet_receive(struct receive_queue *rq, int budget,
 
 static void free_old_xmit_skbs(struct send_queue *sq, bool in_napi)
 {
-       struct sk_buff *skb;
        unsigned int len;
        unsigned int packets = 0;
        unsigned int bytes = 0;
+       void *ptr;
 
-       while ((skb = virtqueue_get_buf(sq->vq, &len)) != NULL) {
-               pr_debug("Sent skb %p\n", skb);
+       while ((ptr = virtqueue_get_buf(sq->vq, &len)) != NULL) {
+               if (likely(!is_xdp_frame(ptr))) {
+                       struct sk_buff *skb = ptr;
 
-               bytes += skb->len;
-               packets++;
+                       pr_debug("Sent skb %p\n", skb);
+
+                       bytes += skb->len;
+                       napi_consume_skb(skb, in_napi);
+               } else {
+                       struct xdp_frame *frame = ptr_to_xdp(ptr);
 
-               napi_consume_skb(skb, in_napi);
+                       bytes += frame->len;
+                       xdp_return_frame(frame);
+               }
+               packets++;
        }
 
        /* Avoid overhead when no packets have been processed
@@ -1359,6 +1398,16 @@ static void free_old_xmit_skbs(struct send_queue *sq, bool in_napi)
        u64_stats_update_end(&sq->stats.syncp);
 }
 
+static bool is_xdp_raw_buffer_queue(struct virtnet_info *vi, int q)
+{
+       if (q < (vi->curr_queue_pairs - vi->xdp_queue_pairs))
+               return false;
+       else if (q < vi->curr_queue_pairs)
+               return true;
+       else
+               return false;
+}
+
 static void virtnet_poll_cleantx(struct receive_queue *rq)
 {
        struct virtnet_info *vi = rq->vq->vdev->priv;
@@ -1366,7 +1415,7 @@ static void virtnet_poll_cleantx(struct receive_queue *rq)
        struct send_queue *sq = &vi->sq[index];
        struct netdev_queue *txq = netdev_get_tx_queue(vi->dev, index);
 
-       if (!sq->napi.weight)
+       if (!sq->napi.weight || is_xdp_raw_buffer_queue(vi, index))
                return;
 
        if (__netif_tx_trylock(txq)) {
@@ -1443,8 +1492,16 @@ static int virtnet_poll_tx(struct napi_struct *napi, int budget)
 {
        struct send_queue *sq = container_of(napi, struct send_queue, napi);
        struct virtnet_info *vi = sq->vq->vdev->priv;
-       struct netdev_queue *txq = netdev_get_tx_queue(vi->dev, vq2txq(sq->vq));
+       unsigned int index = vq2txq(sq->vq);
+       struct netdev_queue *txq;
 
+       if (unlikely(is_xdp_raw_buffer_queue(vi, index))) {
+               /* We don't need to enable cb for XDP */
+               napi_complete_done(napi, 0);
+               return 0;
+       }
+
+       txq = netdev_get_tx_queue(vi->dev, index);
        __netif_tx_lock(txq, raw_smp_processor_id());
        free_old_xmit_skbs(sq, true);
        __netif_tx_unlock(txq);
@@ -2396,6 +2453,10 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog,
                return -ENOMEM;
        }
 
+       old_prog = rtnl_dereference(vi->rq[0].xdp_prog);
+       if (!prog && !old_prog)
+               return 0;
+
        if (prog) {
                prog = bpf_prog_add(prog, vi->max_queue_pairs - 1);
                if (IS_ERR(prog))
@@ -2403,36 +2464,62 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog,
        }
 
        /* Make sure NAPI is not using any XDP TX queues for RX. */
-       if (netif_running(dev))
-               for (i = 0; i < vi->max_queue_pairs; i++)
+       if (netif_running(dev)) {
+               for (i = 0; i < vi->max_queue_pairs; i++) {
                        napi_disable(&vi->rq[i].napi);
+                       virtnet_napi_tx_disable(&vi->sq[i].napi);
+               }
+       }
+
+       if (!prog) {
+               for (i = 0; i < vi->max_queue_pairs; i++) {
+                       rcu_assign_pointer(vi->rq[i].xdp_prog, prog);
+                       if (i == 0)
+                               virtnet_restore_guest_offloads(vi);
+               }
+               synchronize_net();
+       }
 
-       netif_set_real_num_rx_queues(dev, curr_qp + xdp_qp);
        err = _virtnet_set_queues(vi, curr_qp + xdp_qp);
        if (err)
                goto err;
+       netif_set_real_num_rx_queues(dev, curr_qp + xdp_qp);
        vi->xdp_queue_pairs = xdp_qp;
 
-       for (i = 0; i < vi->max_queue_pairs; i++) {
-               old_prog = rtnl_dereference(vi->rq[i].xdp_prog);
-               rcu_assign_pointer(vi->rq[i].xdp_prog, prog);
-               if (i == 0) {
-                       if (!old_prog)
+       if (prog) {
+               for (i = 0; i < vi->max_queue_pairs; i++) {
+                       rcu_assign_pointer(vi->rq[i].xdp_prog, prog);
+                       if (i == 0 && !old_prog)
                                virtnet_clear_guest_offloads(vi);
-                       if (!prog)
-                               virtnet_restore_guest_offloads(vi);
                }
+       }
+
+       for (i = 0; i < vi->max_queue_pairs; i++) {
                if (old_prog)
                        bpf_prog_put(old_prog);
-               if (netif_running(dev))
+               if (netif_running(dev)) {
                        virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi);
+                       virtnet_napi_tx_enable(vi, vi->sq[i].vq,
+                                              &vi->sq[i].napi);
+               }
        }
 
        return 0;
 
 err:
-       for (i = 0; i < vi->max_queue_pairs; i++)
-               virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi);
+       if (!prog) {
+               virtnet_clear_guest_offloads(vi);
+               for (i = 0; i < vi->max_queue_pairs; i++)
+                       rcu_assign_pointer(vi->rq[i].xdp_prog, old_prog);
+       }
+
+       if (netif_running(dev)) {
+               for (i = 0; i < vi->max_queue_pairs; i++) {
+                       virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi);
+                       virtnet_napi_tx_enable(vi, vi->sq[i].vq,
+                                              &vi->sq[i].napi);
+               }
+       }
        if (prog)
                bpf_prog_sub(prog, vi->max_queue_pairs - 1);
        return err;
@@ -2614,16 +2701,6 @@ static void free_receive_page_frags(struct virtnet_info *vi)
                        put_page(vi->rq[i].alloc_frag.page);
 }
 
-static bool is_xdp_raw_buffer_queue(struct virtnet_info *vi, int q)
-{
-       if (q < (vi->curr_queue_pairs - vi->xdp_queue_pairs))
-               return false;
-       else if (q < vi->curr_queue_pairs)
-               return true;
-       else
-               return false;
-}
-
 static void free_unused_bufs(struct virtnet_info *vi)
 {
        void *buf;
@@ -2632,10 +2709,10 @@ static void free_unused_bufs(struct virtnet_info *vi)
        for (i = 0; i < vi->max_queue_pairs; i++) {
                struct virtqueue *vq = vi->sq[i].vq;
                while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) {
-                       if (!is_xdp_raw_buffer_queue(vi, i))
+                       if (!is_xdp_frame(buf))
                                dev_kfree_skb(buf);
                        else
-                               put_page(virt_to_head_page(buf));
+                               xdp_return_frame(ptr_to_xdp(buf));
                }
        }
 
index ef45c3c..33edc78 100644 (file)
@@ -869,6 +869,14 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f,
        call_rcu(&f->rcu, vxlan_fdb_free);
 }
 
+static void vxlan_dst_free(struct rcu_head *head)
+{
+       struct vxlan_rdst *rd = container_of(head, struct vxlan_rdst, rcu);
+
+       dst_cache_destroy(&rd->dst_cache);
+       kfree(rd);
+}
+
 static int vxlan_fdb_update_existing(struct vxlan_dev *vxlan,
                                     union vxlan_addr *ip,
                                     __u16 state, __u16 flags,
@@ -941,8 +949,10 @@ static int vxlan_fdb_update_existing(struct vxlan_dev *vxlan,
 err_notify:
        if ((flags & NLM_F_REPLACE) && rc)
                *rd = oldrd;
-       else if ((flags & NLM_F_APPEND) && rc)
+       else if ((flags & NLM_F_APPEND) && rc) {
                list_del_rcu(&rd->list);
+               call_rcu(&rd->rcu, vxlan_dst_free);
+       }
        return err;
 }
 
@@ -1013,14 +1023,6 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
        }
 }
 
-static void vxlan_dst_free(struct rcu_head *head)
-{
-       struct vxlan_rdst *rd = container_of(head, struct vxlan_rdst, rcu);
-
-       dst_cache_destroy(&rd->dst_cache);
-       kfree(rd);
-}
-
 static void vxlan_fdb_dst_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f,
                                  struct vxlan_rdst *rd, bool swdev_notify)
 {
@@ -2291,7 +2293,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
        struct pcpu_sw_netstats *tx_stats, *rx_stats;
        union vxlan_addr loopback;
        union vxlan_addr *remote_ip = &dst_vxlan->default_dst.remote_ip;
-       struct net_device *dev = skb->dev;
+       struct net_device *dev;
        int len = skb->len;
 
        tx_stats = this_cpu_ptr(src_vxlan->dev->tstats);
@@ -2311,9 +2313,15 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
 #endif
        }
 
+       rcu_read_lock();
+       dev = skb->dev;
+       if (unlikely(!(dev->flags & IFF_UP))) {
+               kfree_skb(skb);
+               goto drop;
+       }
+
        if (dst_vxlan->cfg.flags & VXLAN_F_LEARN)
-               vxlan_snoop(skb->dev, &loopback, eth_hdr(skb)->h_source, 0,
-                           vni);
+               vxlan_snoop(dev, &loopback, eth_hdr(skb)->h_source, 0, vni);
 
        u64_stats_update_begin(&tx_stats->syncp);
        tx_stats->tx_packets++;
@@ -2326,8 +2334,10 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
                rx_stats->rx_bytes += len;
                u64_stats_update_end(&rx_stats->syncp);
        } else {
+drop:
                dev->stats.rx_dropped++;
        }
+       rcu_read_unlock();
 }
 
 static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
index d5dc823..fa78d2b 100644 (file)
@@ -1575,7 +1575,7 @@ try:
                                        dev->stats.tx_packets++;
                                        dev->stats.tx_bytes += skb->len;
                                }
-                               dev_kfree_skb_irq(skb);
+                               dev_consume_skb_irq(skb);
                                dpriv->tx_skbuff[cur] = NULL;
                                ++dpriv->tx_dirty;
                        } else {
index 66d889d..a08f04c 100644 (file)
@@ -482,7 +482,7 @@ static int hdlc_tx_done(struct ucc_hdlc_private *priv)
                memset(priv->tx_buffer +
                       (be32_to_cpu(bd->buf) - priv->dma_tx_addr),
                       0, skb->len);
-               dev_kfree_skb_irq(skb);
+               dev_consume_skb_irq(skb);
 
                priv->tx_skbuff[priv->skb_dirtytx] = NULL;
                priv->skb_dirtytx =
index d573a57..459ce52 100644 (file)
@@ -565,7 +565,7 @@ static int wanxl_pci_init_one(struct pci_dev *pdev,
        u32 plx_phy;            /* PLX PCI base address */
        u32 mem_phy;            /* memory PCI base addr */
        u8 __iomem *mem;        /* memory virtual base addr */
-       int i, ports, alloc_size;
+       int i, ports;
 
 #ifndef MODULE
        pr_info_once("%s\n", version);
@@ -601,8 +601,7 @@ static int wanxl_pci_init_one(struct pci_dev *pdev,
        default: ports = 4;
        }
 
-       alloc_size = sizeof(struct card) + ports * sizeof(struct port);
-       card = kzalloc(alloc_size, GFP_KERNEL);
+       card = kzalloc(struct_size(card, ports, ports), GFP_KERNEL);
        if (card == NULL) {
                pci_release_regions(pdev);
                pci_disable_device(pdev);
index 0b60295..d28b96d 100644 (file)
@@ -1260,8 +1260,8 @@ int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb)
                goto error_msg_hdr_check;
        result = -EIO;
        num_pls = le16_to_cpu(msg_hdr->num_pls);
-       pl_itr = sizeof(*msg_hdr) +     /* Check payload descriptor(s) */
-               num_pls * sizeof(msg_hdr->pld[0]);
+       /* Check payload descriptor(s) */
+       pl_itr = struct_size(msg_hdr, pld, num_pls);
        pl_itr = ALIGN(pl_itr, I2400M_PL_ALIGN);
        if (pl_itr > skb_len) { /* got all the payload descriptors? */
                dev_err(dev, "RX: HW BUG? message too short (%u bytes) for "
index f8eb66e..73842a8 100644 (file)
@@ -210,6 +210,7 @@ retry:
                        msleep(10);     /* give the device some time */
                        goto retry;
                }
+               /* fall through */
        case -EINVAL:                   /* while removing driver */
        case -ENODEV:                   /* dev disconnect ... */
        case -ENOENT:                   /* just ignore it */
index 399b501..e8891f5 100644 (file)
@@ -548,7 +548,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
        {
                .id = WCN3990_HW_1_0_DEV_VERSION,
                .dev_id = 0,
-               .bus = ATH10K_BUS_PCI,
+               .bus = ATH10K_BUS_SNOC,
                .name = "wcn3990 hw1.0",
                .continuous_frag_desc = true,
                .tx_chain_mask = 0x7,
index 2034ccc..021eb30 100644 (file)
@@ -1986,7 +1986,7 @@ static inline const char *ath10k_wmi_phymode_str(enum wmi_phy_mode mode)
                /* no default handler to allow compiler to check that the
                 * enum is fully handled
                 */
-       };
+       }
 
        return "<unknown>";
 }
index 54132af..aa1c71a 100644 (file)
@@ -1140,7 +1140,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
 
                len -= ie_len;
                data += ie_len;
-       };
+       }
 
        ret = 0;
 out:
index 7704638..976c8ec 100644 (file)
@@ -668,15 +668,13 @@ static void b43_remove_dynamic_debug(struct b43_wldev *dev)
 static void b43_add_dynamic_debug(struct b43_wldev *dev)
 {
        struct b43_dfsentry *e = dev->dfsentry;
-       struct dentry *d;
 
-#define add_dyn_dbg(name, id, initstate) do {          \
-       e->dyn_debug[id] = (initstate);                 \
-       d = debugfs_create_bool(name, 0600, e->subdir,  \
-                               &(e->dyn_debug[id]));   \
-       if (!IS_ERR(d))                                 \
-               e->dyn_debug_dentries[id] = d;          \
-                               } while (0)
+#define add_dyn_dbg(name, id, initstate) do {                  \
+       e->dyn_debug[id] = (initstate);                         \
+       e->dyn_debug_dentries[id] =                             \
+               debugfs_create_bool(name, 0600, e->subdir,      \
+                               &(e->dyn_debug[id]));           \
+       } while (0)
 
        add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, false);
        add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, false);
@@ -718,19 +716,6 @@ void b43_debugfs_add_device(struct b43_wldev *dev)
 
        snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
        e->subdir = debugfs_create_dir(devdir, rootdir);
-       if (!e->subdir || IS_ERR(e->subdir)) {
-               if (e->subdir == ERR_PTR(-ENODEV)) {
-                       b43dbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not "
-                              "enabled in kernel config\n");
-               } else {
-                       b43err(dev->wl, "debugfs: cannot create %s directory\n",
-                              devdir);
-               }
-               dev->dfsentry = NULL;
-               kfree(log->log);
-               kfree(e);
-               return;
-       }
 
        e->mmio16read_next = 0xFFFF; /* invalid address */
        e->mmio32read_next = 0xFFFF; /* invalid address */
@@ -741,13 +726,10 @@ void b43_debugfs_add_device(struct b43_wldev *dev)
 
 #define ADD_FILE(name, mode)   \
        do {                                                    \
-               struct dentry *d;                               \
-               d = debugfs_create_file(__stringify(name),      \
+               e->file_##name.dentry =                         \
+                       debugfs_create_file(__stringify(name),  \
                                        mode, e->subdir, dev,   \
                                        &fops_##name.fops);     \
-               e->file_##name.dentry = NULL;                   \
-               if (!IS_ERR(d))                                 \
-                       e->file_##name.dentry = d;              \
        } while (0)
 
 
@@ -818,8 +800,6 @@ void b43_debugfs_log_txstat(struct b43_wldev *dev,
 void b43_debugfs_init(void)
 {
        rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
-       if (IS_ERR(rootdir))
-               rootdir = NULL;
 }
 
 void b43_debugfs_exit(void)
index 82ef56e..8150ade 100644 (file)
@@ -361,15 +361,13 @@ static void b43legacy_remove_dynamic_debug(struct b43legacy_wldev *dev)
 static void b43legacy_add_dynamic_debug(struct b43legacy_wldev *dev)
 {
        struct b43legacy_dfsentry *e = dev->dfsentry;
-       struct dentry *d;
 
-#define add_dyn_dbg(name, id, initstate) do {          \
-       e->dyn_debug[id] = (initstate);                 \
-       d = debugfs_create_bool(name, 0600, e->subdir,  \
-                               &(e->dyn_debug[id]));   \
-       if (!IS_ERR(d))                                 \
-               e->dyn_debug_dentries[id] = d;          \
-                               } while (0)
+#define add_dyn_dbg(name, id, initstate) do {                  \
+       e->dyn_debug[id] = (initstate);                         \
+       e->dyn_debug_dentries[id] =                             \
+               debugfs_create_bool(name, 0600, e->subdir,      \
+                               &(e->dyn_debug[id]));           \
+       } while (0)
 
        add_dyn_dbg("debug_xmitpower", B43legacy_DBG_XMITPOWER, false);
        add_dyn_dbg("debug_dmaoverflow", B43legacy_DBG_DMAOVERFLOW, false);
@@ -408,29 +406,14 @@ void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev)
 
        snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
        e->subdir = debugfs_create_dir(devdir, rootdir);
-       if (!e->subdir || IS_ERR(e->subdir)) {
-               if (e->subdir == ERR_PTR(-ENODEV)) {
-                       b43legacydbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not "
-                              "enabled in kernel config\n");
-               } else {
-                       b43legacyerr(dev->wl, "debugfs: cannot create %s directory\n",
-                              devdir);
-               }
-               dev->dfsentry = NULL;
-               kfree(log->log);
-               kfree(e);
-               return;
-       }
 
 #define ADD_FILE(name, mode)   \
        do {                                                    \
-               struct dentry *d;                               \
-               d = debugfs_create_file(__stringify(name),      \
+               e->file_##name.dentry =                         \
+                       debugfs_create_file(__stringify(name),  \
                                        mode, e->subdir, dev,   \
                                        &fops_##name.fops);     \
                e->file_##name.dentry = NULL;                   \
-               if (!IS_ERR(d))                                 \
-                       e->file_##name.dentry = d;              \
        } while (0)
 
 
@@ -492,8 +475,6 @@ void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev,
 void b43legacy_debugfs_init(void)
 {
        rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
-       if (IS_ERR(rootdir))
-               rootdir = NULL;
 }
 
 void b43legacy_debugfs_exit(void)
index 22fd95a..f7cf3e5 100644 (file)
@@ -16,8 +16,8 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 ccflags-y += \
-       -Idrivers/net/wireless/broadcom/brcm80211/brcmfmac      \
-       -Idrivers/net/wireless/broadcom/brcm80211/include
+       -I $(srctree)/$(src) \
+       -I $(srctree)/$(src)/../include
 
 obj-$(CONFIG_BRCMFMAC) += brcmfmac.o
 brcmfmac-objs += \
index d64bf23..ec12986 100644 (file)
@@ -315,7 +315,7 @@ static int brcmf_sdiod_skbuff_read(struct brcmf_sdio_dev *sdiodev,
                /* bail out as things are really fishy here */
                WARN(1, "invalid sdio function number: %d\n", func->num);
                err = -ENOMEDIUM;
-       };
+       }
 
        if (err == -ENOMEDIUM)
                brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
index 1f1e95a..0ce1d81 100644 (file)
@@ -149,7 +149,7 @@ static int brcmf_c_process_clm_blob(struct brcmf_if *ifp)
                return err;
        }
 
-       err = request_firmware(&clm, clm_name, bus->dev);
+       err = firmware_request_nowarn(&clm, clm_name, bus->dev);
        if (err) {
                brcmf_info("no clm_blob available (err=%d), device may have limited channels available\n",
                           err);
index 51d76ac..7535cb0 100644 (file)
@@ -43,6 +43,10 @@ static const struct brcmf_dmi_data meegopad_t08_data = {
        BRCM_CC_43340_CHIP_ID, 2, "meegopad-t08"
 };
 
+static const struct brcmf_dmi_data pov_tab_p1006w_data = {
+       BRCM_CC_43340_CHIP_ID, 2, "pov-tab-p1006w-data"
+};
+
 static const struct dmi_system_id dmi_platform_data[] = {
        {
                /* Match for the GPDwin which unfortunately uses somewhat
@@ -81,6 +85,17 @@ static const struct dmi_system_id dmi_platform_data[] = {
                },
                .driver_data = (void *)&meegopad_t08_data,
        },
+       {
+               /* Point of View TAB-P1006W-232 */
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BayTrail"),
+                       /* Note 105b is Foxcon's USB/PCI vendor id */
+                       DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "105B"),
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"),
+               },
+               .driver_data = (void *)&pov_tab_p1006w_data,
+       },
        {}
 };
 
index ffa243e..37b4038 100644 (file)
@@ -496,6 +496,11 @@ int brcmf_pno_stop_sched_scan(struct brcmf_if *ifp, u64 reqid)
        brcmf_dbg(TRACE, "reqid=%llu\n", reqid);
 
        pi = ifp_to_pno(ifp);
+
+       /* No PNO request */
+       if (!pi->n_reqs)
+               return 0;
+
        err = brcmf_pno_remove_request(pi, reqid);
        if (err)
                return err;
index a4308c6..76cfaf6 100644 (file)
@@ -1550,6 +1550,10 @@ void brcmf_usb_exit(void)
 
 void brcmf_usb_register(void)
 {
+       int ret;
+
        brcmf_dbg(USB, "Enter\n");
-       usb_register(&brcmf_usbdrvr);
+       ret = usb_register(&brcmf_usbdrvr);
+       if (ret)
+               brcmf_err("usb_register failed %d\n", ret);
 }
index ed83f33..482d773 100644 (file)
@@ -16,9 +16,9 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 ccflags-y := \
-       -Idrivers/net/wireless/broadcom/brcm80211/brcmsmac \
-       -Idrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy \
-       -Idrivers/net/wireless/broadcom/brcm80211/include
+       -I $(srctree)/$(src) \
+       -I $(srctree)/$(src)/phy \
+       -I $(srctree)/$(src)/../include
 
 brcmsmac-y := \
        mac80211_if.o \
index 3bd54f1..6d776ef 100644 (file)
@@ -37,27 +37,18 @@ static struct dentry *root_folder;
 void brcms_debugfs_init(void)
 {
        root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL);
-       if (IS_ERR(root_folder))
-               root_folder = NULL;
 }
 
 void brcms_debugfs_exit(void)
 {
-       if (!root_folder)
-               return;
-
        debugfs_remove_recursive(root_folder);
        root_folder = NULL;
 }
 
-int brcms_debugfs_attach(struct brcms_pub *drvr)
+void brcms_debugfs_attach(struct brcms_pub *drvr)
 {
-       if (!root_folder)
-               return -ENODEV;
-
        drvr->dbgfs_dir = debugfs_create_dir(
                 dev_name(&drvr->wlc->hw->d11core->dev), root_folder);
-       return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
 }
 
 void brcms_debugfs_detach(struct brcms_pub *drvr)
@@ -195,7 +186,7 @@ static const struct file_operations brcms_debugfs_def_ops = {
        .llseek = seq_lseek
 };
 
-static int
+static void
 brcms_debugfs_add_entry(struct brcms_pub *drvr, const char *fn,
                        int (*read_fn)(struct seq_file *seq, void *data))
 {
@@ -203,27 +194,18 @@ brcms_debugfs_add_entry(struct brcms_pub *drvr, const char *fn,
        struct dentry *dentry =  drvr->dbgfs_dir;
        struct brcms_debugfs_entry *entry;
 
-       if (IS_ERR_OR_NULL(dentry))
-               return -ENOENT;
-
        entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
        if (!entry)
-               return -ENOMEM;
+               return;
 
        entry->read = read_fn;
        entry->drvr = drvr;
 
-       dentry = debugfs_create_file(fn, 0444, dentry, entry,
-                                    &brcms_debugfs_def_ops);
-
-       return PTR_ERR_OR_ZERO(dentry);
+       debugfs_create_file(fn, 0444, dentry, entry, &brcms_debugfs_def_ops);
 }
 
 void brcms_debugfs_create_files(struct brcms_pub *drvr)
 {
-       if (IS_ERR_OR_NULL(drvr->dbgfs_dir))
-               return;
-
        brcms_debugfs_add_entry(drvr, "hardware", brcms_debugfs_hardware_read);
        brcms_debugfs_add_entry(drvr, "macstat", brcms_debugfs_macstat_read);
 }
index 822781c..56898e6 100644 (file)
@@ -68,7 +68,7 @@ void __brcms_dbg(struct device *dev, u32 level, const char *func,
 struct brcms_pub;
 void brcms_debugfs_init(void);
 void brcms_debugfs_exit(void);
-int brcms_debugfs_attach(struct brcms_pub *drvr);
+void brcms_debugfs_attach(struct brcms_pub *drvr);
 void brcms_debugfs_detach(struct brcms_pub *drvr);
 struct dentry *brcms_debugfs_get_devdir(struct brcms_pub *drvr);
 void brcms_debugfs_create_files(struct brcms_pub *drvr);
index e78a93a..c6e107f 100644 (file)
@@ -1199,8 +1199,6 @@ wlc_lcnphy_rx_iq_est(struct brcms_phy *pi,
 {
        int wait_count = 0;
        bool result = true;
-       u8 phybw40;
-       phybw40 = CHSPEC_IS40(pi->radio_chanspec);
 
        mod_phy_reg(pi, 0x6da, (0x1 << 5), (1) << 5);
 
@@ -3082,7 +3080,7 @@ static void wlc_lcnphy_tx_pwr_ctrl_init(struct brcms_phy_pub *ppi)
        u8 bbmult;
        struct phytbl_info tab;
        s32 a1, b0, b1;
-       s32 tssi, pwr, maxtargetpwr, mintargetpwr;
+       s32 tssi, pwr, mintargetpwr;
        bool suspend;
        struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro);
 
@@ -3119,7 +3117,6 @@ static void wlc_lcnphy_tx_pwr_ctrl_init(struct brcms_phy_pub *ppi)
                b0 = pi->txpa_2g[0];
                b1 = pi->txpa_2g[1];
                a1 = pi->txpa_2g[2];
-               maxtargetpwr = wlc_lcnphy_tssi2dbm(10, a1, b0, b1);
                mintargetpwr = wlc_lcnphy_tssi2dbm(125, a1, b0, b1);
 
                tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
@@ -4212,7 +4209,7 @@ static void wlc_lcnphy_periodic_cal(struct brcms_phy *pi)
        s8 index;
        struct phytbl_info tab;
        s32 a1, b0, b1;
-       s32 tssi, pwr, maxtargetpwr, mintargetpwr;
+       s32 tssi, pwr, mintargetpwr;
        struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
 
        pi->phy_lastcal = pi->sh->now;
@@ -4249,7 +4246,6 @@ static void wlc_lcnphy_periodic_cal(struct brcms_phy *pi)
                b0 = pi->txpa_2g[0];
                b1 = pi->txpa_2g[1];
                a1 = pi->txpa_2g[2];
-               maxtargetpwr = wlc_lcnphy_tssi2dbm(10, a1, b0, b1);
                mintargetpwr = wlc_lcnphy_tssi2dbm(125, a1, b0, b1);
 
                tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
@@ -4622,13 +4618,10 @@ static void wlc_lcnphy_radio_init(struct brcms_phy *pi)
 static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
 {
        uint idx;
-       u8 phybw40;
        struct phytbl_info tab;
        const struct phytbl_info *tb;
        u32 val;
 
-       phybw40 = CHSPEC_IS40(pi->radio_chanspec);
-
        for (idx = 0; idx < dot11lcnphytbl_info_sz_rev0; idx++)
                wlc_lcnphy_write_table(pi, &dot11lcnphytbl_info_rev0[idx]);
 
@@ -4831,9 +4824,7 @@ static void wlc_lcnphy_baseband_init(struct brcms_phy *pi)
 
 void wlc_phy_init_lcnphy(struct brcms_phy *pi)
 {
-       u8 phybw40;
        struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy;
-       phybw40 = CHSPEC_IS40(pi->radio_chanspec);
 
        pi_lcn->lcnphy_cal_counter = 0;
        pi_lcn->lcnphy_cal_temper = pi_lcn->lcnphy_rawtempsense;
index 256c91f..bb02c62 100644 (file)
@@ -15,9 +15,7 @@
 # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-ccflags-y :=                           \
-       -Idrivers/net/wireless/broadcom/brcm80211/brcmutil \
-       -Idrivers/net/wireless/broadcom/brcm80211/include
+ccflags-y := -I $(srctree)/$(src)/../include
 
 obj-$(CONFIG_BRCMUTIL) += brcmutil.o
 brcmutil-objs  = utils.o d11.o
index 57e3b6c..271977f 100644 (file)
@@ -3756,10 +3756,7 @@ il3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (err)
                goto out_remove_sysfs;
 
-       err = il_dbgfs_register(il, DRV_NAME);
-       if (err)
-               IL_ERR("failed to create debugfs files. Ignoring error: %d\n",
-                      err);
+       il_dbgfs_register(il, DRV_NAME);
 
        /* Start monitoring the killswitch */
        queue_delayed_work(il->workqueue, &il->_3945.rfkill_poll, 2 * HZ);
index 6b4488a..94222ae 100644 (file)
@@ -4988,10 +4988,7 @@ il4965_ucode_callback(const struct firmware *ucode_raw, void *context)
        if (err)
                goto out_unbind;
 
-       err = il_dbgfs_register(il, DRV_NAME);
-       if (err)
-               IL_ERR("failed to create debugfs files. Ignoring error: %d\n",
-                      err);
+       il_dbgfs_register(il, DRV_NAME);
 
        err = sysfs_create_group(&il->pci_dev->dev.kobj, &il_attribute_group);
        if (err) {
index dc6a74a..b079c64 100644 (file)
@@ -2974,13 +2974,11 @@ il_print_hex_dump(struct il_priv *il, int level, const void *p, u32 len)
 #endif /* CONFIG_IWLEGACY_DEBUG */
 
 #ifdef CONFIG_IWLEGACY_DEBUGFS
-int il_dbgfs_register(struct il_priv *il, const char *name);
+void il_dbgfs_register(struct il_priv *il, const char *name);
 void il_dbgfs_unregister(struct il_priv *il);
 #else
-static inline int
-il_dbgfs_register(struct il_priv *il, const char *name)
+static inline void il_dbgfs_register(struct il_priv *il, const char *name)
 {
-       return 0;
 }
 
 static inline void
index d76073d..fa21141 100644 (file)
@@ -128,23 +128,12 @@ EXPORT_SYMBOL(il_update_stats);
 
 /* create and remove of files */
 #define DEBUGFS_ADD_FILE(name, parent, mode) do {                      \
-       if (!debugfs_create_file(#name, mode, parent, il,               \
-                        &il_dbgfs_##name##_ops))               \
-               goto err;                                               \
+       debugfs_create_file(#name, mode, parent, il,                    \
+                           &il_dbgfs_##name##_ops);                    \
 } while (0)
 
 #define DEBUGFS_ADD_BOOL(name, parent, ptr) do {                       \
-       struct dentry *__tmp;                                           \
-       __tmp = debugfs_create_bool(#name, 0600, parent, ptr);          \
-       if (IS_ERR(__tmp) || !__tmp)                                    \
-               goto err;                                               \
-} while (0)
-
-#define DEBUGFS_ADD_X32(name, parent, ptr) do {                                \
-       struct dentry *__tmp;                                           \
-       __tmp = debugfs_create_x32(#name, 0600, parent, ptr);           \
-       if (IS_ERR(__tmp) || !__tmp)                                    \
-               goto err;                                               \
+       debugfs_create_bool(#name, 0600, parent, ptr);                  \
 } while (0)
 
 /* file operation */
@@ -1341,27 +1330,18 @@ DEBUGFS_WRITE_FILE_OPS(wd_timeout);
  * Create the debugfs files and directories
  *
  */
-int
+void
 il_dbgfs_register(struct il_priv *il, const char *name)
 {
        struct dentry *phyd = il->hw->wiphy->debugfsdir;
        struct dentry *dir_drv, *dir_data, *dir_rf, *dir_debug;
 
        dir_drv = debugfs_create_dir(name, phyd);
-       if (!dir_drv)
-               return -ENOMEM;
-
        il->debugfs_dir = dir_drv;
 
        dir_data = debugfs_create_dir("data", dir_drv);
-       if (!dir_data)
-               goto err;
        dir_rf = debugfs_create_dir("rf", dir_drv);
-       if (!dir_rf)
-               goto err;
        dir_debug = debugfs_create_dir("debug", dir_drv);
-       if (!dir_debug)
-               goto err;
 
        DEBUGFS_ADD_FILE(nvm, dir_data, 0400);
        DEBUGFS_ADD_FILE(sram, dir_data, 0600);
@@ -1399,12 +1379,6 @@ il_dbgfs_register(struct il_priv *il, const char *name)
                DEBUGFS_ADD_BOOL(disable_chain_noise, dir_rf,
                                 &il->disable_chain_noise_cal);
        DEBUGFS_ADD_BOOL(disable_tx_power, dir_rf, &il->disable_tx_power_cal);
-       return 0;
-
-err:
-       IL_ERR("Can't create the debugfs directory\n");
-       il_dbgfs_unregister(il);
-       return -ENOMEM;
 }
 EXPORT_SYMBOL(il_dbgfs_register);
 
index 491ca3c..83d5bce 100644 (file)
@@ -1,6 +1,6 @@
 config IWLWIFI
        tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) "
-       depends on PCI && HAS_IOMEM
+       depends on PCI && HAS_IOMEM && CFG80211
        select FW_LOADER
        ---help---
          Select to build the driver supporting the:
@@ -47,6 +47,7 @@ if IWLWIFI
 config IWLWIFI_LEDS
        bool
        depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI
+       depends on IWLMVM || IWLDVM
        select LEDS_TRIGGERS
        select MAC80211_LEDS
        default y
index 7e65073..eb93711 100644 (file)
@@ -83,6 +83,7 @@
 #define IWL_22000_HR_A0_FW_PRE         "iwlwifi-QuQnj-a0-hr-a0-"
 #define IWL_22000_SU_Z0_FW_PRE         "iwlwifi-su-z0-"
 #define IWL_QU_B_JF_B_FW_PRE           "iwlwifi-Qu-b0-jf-b0-"
+#define IWL_CC_A_FW_PRE                        "iwlwifi-cc-a0-"
 
 #define IWL_22000_HR_MODULE_FIRMWARE(api) \
        IWL_22000_HR_FW_PRE __stringify(api) ".ucode"
        IWL_22000_SU_Z0_FW_PRE __stringify(api) ".ucode"
 #define IWL_QU_B_JF_B_MODULE_FIRMWARE(api) \
        IWL_QU_B_JF_B_FW_PRE __stringify(api) ".ucode"
+#define IWL_CC_A_MODULE_FIRMWARE(api) \
+       IWL_CC_A_FW_PRE __stringify(api) ".ucode"
 
 static const struct iwl_base_params iwl_22000_base_params = {
        .eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
@@ -195,8 +198,8 @@ const struct iwl_cfg iwl22000_2ac_cfg_jf = {
        IWL_DEVICE_22500,
 };
 
-const struct iwl_cfg iwl22000_2ax_cfg_hr = {
-       .name = "Intel(R) Dual Band Wireless AX 22000",
+const struct iwl_cfg iwl22560_2ax_cfg_hr = {
+       .name = "Intel(R) Wireless-AX 22560",
        .fw_name_pre = IWL_22000_QU_B_HR_B_FW_PRE,
        IWL_DEVICE_22500,
        /*
@@ -207,6 +210,42 @@ const struct iwl_cfg iwl22000_2ax_cfg_hr = {
        .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
 };
 
+const struct iwl_cfg iwl22260_2ax_cfg = {
+       .name = "Intel(R) Wireless-AX 22260",
+       .fw_name_pre = IWL_CC_A_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,
+};
+
+const struct iwl_cfg killer1650x_2ax_cfg = {
+       .name = "Killer(R) Wireless-AX 1650x Wireless Network Adapter (22260NGW)",
+       .fw_name_pre = IWL_CC_A_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,
+};
+
+const struct iwl_cfg killer1650w_2ax_cfg = {
+       .name = "Killer(R) Wireless-AX 1650w Wireless Network Adapter (22260D2W)",
+       .fw_name_pre = IWL_CC_A_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,
+};
+
 /*
  * All JF radio modules are part of the 9000 series, but the MAC part
  * looks more like 22000.  That's why this device is here, but called
@@ -230,6 +269,12 @@ const struct iwl_cfg iwl9560_2ac_cfg_qu_b0_jf_b0 = {
        IWL_DEVICE_22500,
 };
 
+const struct iwl_cfg iwl9560_2ac_160_cfg_qu_b0_jf_b0 = {
+       .name = "Intel(R) Wireless-AC 9560 160MHz",
+       .fw_name_pre = IWL_QU_B_JF_B_FW_PRE,
+       IWL_DEVICE_22500,
+};
+
 const struct iwl_cfg killer1550i_2ac_cfg_qu_b0_jf_b0 = {
        .name = "Killer (R) Wireless-AC 1550i Wireless Network Adapter (9560NGW)",
        .fw_name_pre = IWL_QU_B_JF_B_FW_PRE,
@@ -242,6 +287,30 @@ const struct iwl_cfg killer1550s_2ac_cfg_qu_b0_jf_b0 = {
        IWL_DEVICE_22500,
 };
 
+const struct iwl_cfg killer1650s_2ax_cfg_qu_b0_hr_b0 = {
+       .name = "Killer(R) Wireless-AX 1650i Wireless Network Adapter (22560NGW)",
+       .fw_name_pre = IWL_22000_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,
+};
+
+const struct iwl_cfg killer1650i_2ax_cfg_qu_b0_hr_b0 = {
+       .name = "Killer(R) Wireless-AX 1650s Wireless Network Adapter (22560D2W)",
+       .fw_name_pre = IWL_22000_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,
+};
+
 const struct iwl_cfg iwl22000_2ax_cfg_jf = {
        .name = "Intel(R) Dual Band Wireless AX 22000",
        .fw_name_pre = IWL_QU_B_JF_B_FW_PRE,
@@ -324,3 +393,4 @@ MODULE_FIRMWARE(IWL_22000_JF_B0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_HR_A0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_SU_Z0_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_QU_B_JF_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
+MODULE_FIRMWARE(IWL_CC_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
index f211413..113bcf7 100644 (file)
 #define IWL9000_SMEM_OFFSET            0x400000
 #define IWL9000_SMEM_LEN               0x68000
 
-#define  IWL9000A_FW_PRE "iwlwifi-9000-pu-a0-jf-a0-"
-#define  IWL9000B_FW_PRE "iwlwifi-9000-pu-b0-jf-b0-"
-#define  IWL9000RFB_FW_PRE "iwlwifi-9000-pu-a0-jf-b0-"
-#define  IWL9260A_FW_PRE "iwlwifi-9260-th-a0-jf-a0-"
-#define  IWL9260B_FW_PRE "iwlwifi-9260-th-b0-jf-b0-"
-#define IWL9000A_MODULE_FIRMWARE(api) \
-       IWL9000A_FW_PRE __stringify(api) ".ucode"
-#define IWL9000B_MODULE_FIRMWARE(api) \
-       IWL9000B_FW_PRE __stringify(api) ".ucode"
-#define IWL9000RFB_MODULE_FIRMWARE(api) \
-       IWL9000RFB_FW_PRE __stringify(api) ".ucode"
-#define IWL9260A_MODULE_FIRMWARE(api) \
-       IWL9260A_FW_PRE __stringify(api) ".ucode"
-#define IWL9260B_MODULE_FIRMWARE(api) \
-       IWL9260B_FW_PRE __stringify(api) ".ucode"
+#define  IWL9000_FW_PRE "iwlwifi-9000-pu-b0-jf-b0-"
+#define  IWL9260_FW_PRE "iwlwifi-9260-th-b0-jf-b0-"
+#define IWL9000_MODULE_FIRMWARE(api) \
+       IWL9000_FW_PRE __stringify(api) ".ucode"
+#define IWL9260_MODULE_FIRMWARE(api) \
+       IWL9260_FW_PRE __stringify(api) ".ucode"
 
 static const struct iwl_base_params iwl9000_base_params = {
        .eeprom_size = OTP_LOW_IMAGE_SIZE_32K,
@@ -162,81 +153,87 @@ static const struct iwl_tt_params iwl9000_tt_params = {
 
 const struct iwl_cfg iwl9160_2ac_cfg = {
        .name = "Intel(R) Dual Band Wireless AC 9160",
-       .fw_name_pre = IWL9260A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE,
+       .fw_name_pre = IWL9260_FW_PRE,
        IWL_DEVICE_9000,
 };
 
 const struct iwl_cfg iwl9260_2ac_cfg = {
        .name = "Intel(R) Dual Band Wireless AC 9260",
-       .fw_name_pre = IWL9260A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE,
+       .fw_name_pre = IWL9260_FW_PRE,
+       IWL_DEVICE_9000,
+};
+
+const struct iwl_cfg iwl9260_2ac_160_cfg = {
+       .name = "Intel(R) Wireless-AC 9260 160MHz",
+       .fw_name_pre = IWL9260_FW_PRE,
        IWL_DEVICE_9000,
 };
 
 const struct iwl_cfg iwl9260_killer_2ac_cfg = {
        .name = "Killer (R) Wireless-AC 1550 Wireless Network Adapter (9260NGW)",
-       .fw_name_pre = IWL9260A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE,
+       .fw_name_pre = IWL9260_FW_PRE,
        IWL_DEVICE_9000,
 };
 
 const struct iwl_cfg iwl9270_2ac_cfg = {
        .name = "Intel(R) Dual Band Wireless AC 9270",
-       .fw_name_pre = IWL9260A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE,
+       .fw_name_pre = IWL9260_FW_PRE,
        IWL_DEVICE_9000,
 };
 
 const struct iwl_cfg iwl9460_2ac_cfg = {
        .name = "Intel(R) Dual Band Wireless AC 9460",
-       .fw_name_pre = IWL9260A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE,
+       .fw_name_pre = IWL9260_FW_PRE,
        IWL_DEVICE_9000,
 };
 
 const struct iwl_cfg iwl9460_2ac_cfg_soc = {
        .name = "Intel(R) Dual Band Wireless AC 9460",
-       .fw_name_pre = IWL9000A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-       .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+       .fw_name_pre = IWL9000_FW_PRE,
        IWL_DEVICE_9000,
        .integrated = true,
        .soc_latency = 5000,
 };
 
 const struct iwl_cfg iwl9461_2ac_cfg_soc = {
-               .name = "Intel(R) Dual Band Wireless AC 9461",
-               .fw_name_pre = IWL9000A_FW_PRE,
-               .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-               .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
-               IWL_DEVICE_9000,
-               .integrated = true,
-               .soc_latency = 5000,
+       .name = "Intel(R) Dual Band Wireless AC 9461",
+       .fw_name_pre = IWL9000_FW_PRE,
+       IWL_DEVICE_9000,
+       .integrated = true,
+       .soc_latency = 5000,
 };
 
 const struct iwl_cfg iwl9462_2ac_cfg_soc = {
-               .name = "Intel(R) Dual Band Wireless AC 9462",
-               .fw_name_pre = IWL9000A_FW_PRE,
-               .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-               .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
-               IWL_DEVICE_9000,
-               .integrated = true,
-               .soc_latency = 5000,
+       .name = "Intel(R) Dual Band Wireless AC 9462",
+       .fw_name_pre = IWL9000_FW_PRE,
+       IWL_DEVICE_9000,
+       .integrated = true,
+       .soc_latency = 5000,
 };
 
 const struct iwl_cfg iwl9560_2ac_cfg = {
        .name = "Intel(R) Dual Band Wireless AC 9560",
-       .fw_name_pre = IWL9260A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE,
+       .fw_name_pre = IWL9260_FW_PRE,
+       IWL_DEVICE_9000,
+};
+
+const struct iwl_cfg iwl9560_2ac_160_cfg = {
+       .name = "Intel(R) Wireless-AC 9560 160MHz",
+       .fw_name_pre = IWL9260_FW_PRE,
        IWL_DEVICE_9000,
 };
 
 const struct iwl_cfg iwl9560_2ac_cfg_soc = {
        .name = "Intel(R) Dual Band Wireless AC 9560",
-       .fw_name_pre = IWL9000A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-       .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+       .fw_name_pre = IWL9000_FW_PRE,
+       IWL_DEVICE_9000,
+       .integrated = true,
+       .soc_latency = 5000,
+};
+
+const struct iwl_cfg iwl9560_2ac_160_cfg_soc = {
+       .name = "Intel(R) Wireless-AC 9560 160MHz",
+       .fw_name_pre = IWL9000_FW_PRE,
        IWL_DEVICE_9000,
        .integrated = true,
        .soc_latency = 5000,
@@ -244,9 +241,7 @@ const struct iwl_cfg iwl9560_2ac_cfg_soc = {
 
 const struct iwl_cfg iwl9560_killer_2ac_cfg_soc = {
        .name = "Killer (R) Wireless-AC 1550i Wireless Network Adapter (9560NGW)",
-       .fw_name_pre = IWL9000A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-       .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+       .fw_name_pre = IWL9000_FW_PRE,
        IWL_DEVICE_9000,
        .integrated = true,
        .soc_latency = 5000,
@@ -254,9 +249,7 @@ const struct iwl_cfg iwl9560_killer_2ac_cfg_soc = {
 
 const struct iwl_cfg iwl9560_killer_s_2ac_cfg_soc = {
        .name = "Killer (R) Wireless-AC 1550s Wireless Network Adapter (9560NGW)",
-       .fw_name_pre = IWL9000A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-       .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+       .fw_name_pre = IWL9000_FW_PRE,
        IWL_DEVICE_9000,
        .integrated = true,
        .soc_latency = 5000,
@@ -264,9 +257,7 @@ const struct iwl_cfg iwl9560_killer_s_2ac_cfg_soc = {
 
 const struct iwl_cfg iwl9460_2ac_cfg_shared_clk = {
        .name = "Intel(R) Dual Band Wireless AC 9460",
-       .fw_name_pre = IWL9000A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-       .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+       .fw_name_pre = IWL9000_FW_PRE,
        IWL_DEVICE_9000,
        .integrated = true,
        .soc_latency = 5000,
@@ -275,9 +266,7 @@ const struct iwl_cfg iwl9460_2ac_cfg_shared_clk = {
 
 const struct iwl_cfg iwl9461_2ac_cfg_shared_clk = {
        .name = "Intel(R) Dual Band Wireless AC 9461",
-       .fw_name_pre = IWL9000A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-       .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+       .fw_name_pre = IWL9000_FW_PRE,
        IWL_DEVICE_9000,
        .integrated = true,
        .soc_latency = 5000,
@@ -286,9 +275,7 @@ const struct iwl_cfg iwl9461_2ac_cfg_shared_clk = {
 
 const struct iwl_cfg iwl9462_2ac_cfg_shared_clk = {
        .name = "Intel(R) Dual Band Wireless AC 9462",
-       .fw_name_pre = IWL9000A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-       .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+       .fw_name_pre = IWL9000_FW_PRE,
        IWL_DEVICE_9000,
        .integrated = true,
        .soc_latency = 5000,
@@ -297,9 +284,16 @@ const struct iwl_cfg iwl9462_2ac_cfg_shared_clk = {
 
 const struct iwl_cfg iwl9560_2ac_cfg_shared_clk = {
        .name = "Intel(R) Dual Band Wireless AC 9560",
-       .fw_name_pre = IWL9000A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-       .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+       .fw_name_pre = IWL9000_FW_PRE,
+       IWL_DEVICE_9000,
+       .integrated = true,
+       .soc_latency = 5000,
+       .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
+};
+
+const struct iwl_cfg iwl9560_2ac_160_cfg_shared_clk = {
+       .name = "Intel(R) Wireless-AC 9560 160MHz",
+       .fw_name_pre = IWL9000_FW_PRE,
        IWL_DEVICE_9000,
        .integrated = true,
        .soc_latency = 5000,
@@ -308,9 +302,7 @@ const struct iwl_cfg iwl9560_2ac_cfg_shared_clk = {
 
 const struct iwl_cfg iwl9560_killer_2ac_cfg_shared_clk = {
        .name = "Killer (R) Wireless-AC 1550i Wireless Network Adapter (9560NGW)",
-       .fw_name_pre = IWL9000A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-       .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+       .fw_name_pre = IWL9000_FW_PRE,
        IWL_DEVICE_9000,
        .integrated = true,
        .soc_latency = 5000,
@@ -319,17 +311,12 @@ const struct iwl_cfg iwl9560_killer_2ac_cfg_shared_clk = {
 
 const struct iwl_cfg iwl9560_killer_s_2ac_cfg_shared_clk = {
        .name = "Killer (R) Wireless-AC 1550s Wireless Network Adapter (9560NGW)",
-       .fw_name_pre = IWL9000A_FW_PRE,
-       .fw_name_pre_b_or_c_step = IWL9000B_FW_PRE,
-       .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE,
+       .fw_name_pre = IWL9000_FW_PRE,
        IWL_DEVICE_9000,
        .integrated = true,
        .soc_latency = 5000,
        .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK
 };
 
-MODULE_FIRMWARE(IWL9000A_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
-MODULE_FIRMWARE(IWL9000B_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
-MODULE_FIRMWARE(IWL9000RFB_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
-MODULE_FIRMWARE(IWL9260A_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
-MODULE_FIRMWARE(IWL9260B_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
+MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
+MODULE_FIRMWARE(IWL9260_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX));
index 702d42b..0486b17 100644 (file)
@@ -11,4 +11,4 @@ iwldvm-objs           += rxon.o devices.o
 iwldvm-$(CONFIG_IWLWIFI_LEDS) += led.o
 iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
 
-ccflags-y += -I$(src)/../
+ccflags-y += -I $(srctree)/$(src)/../
index 49b71db..54b759c 100644 (file)
@@ -1,6 +1,7 @@
 /******************************************************************************
  *
  * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2018 Intel Corporation
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -710,24 +711,6 @@ static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
        return ret;
 }
 
-static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg)
-{
-       if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG)
-               return false;
-       return true;
-}
-
-static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg)
-{
-       if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG)
-               return false;
-       if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG)
-               return true;
-
-       /* disabled by default */
-       return false;
-}
-
 static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif,
                                   struct ieee80211_ampdu_params *params)
@@ -752,7 +735,7 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
 
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
-               if (!iwl_enable_rx_ampdu(priv->cfg))
+               if (!iwl_enable_rx_ampdu())
                        break;
                IWL_DEBUG_HT(priv, "start Rx\n");
                ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn);
@@ -764,7 +747,7 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
        case IEEE80211_AMPDU_TX_START:
                if (!priv->trans->ops->txq_enable)
                        break;
-               if (!iwl_enable_tx_ampdu(priv->cfg))
+               if (!iwl_enable_tx_ampdu())
                        break;
                IWL_DEBUG_HT(priv, "start Tx\n");
                ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn);
index c219bca..bd3c3b9 100644 (file)
@@ -1054,7 +1054,7 @@ static void iwl_bg_restart(struct work_struct *data)
                        ieee80211_restart_hw(priv->hw);
                else
                        IWL_ERR(priv,
-                               "Cannot request restart before registrating with mac80211\n");
+                               "Cannot request restart before registering with mac80211\n");
        } else {
                WARN_ON(1);
        }
index 4de2727..a156dcf 100644 (file)
@@ -1,6 +1,7 @@
 /******************************************************************************
  *
  * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2018 Intel Corporation
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -325,9 +326,9 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
                                        iwl_prepare_ct_kill_task(priv);
                                        tt->state = old_state;
                                }
-                       } else if (old_state == IWL_TI_CT_KILL &&
-                                tt->state != IWL_TI_CT_KILL)
+                       } else if (old_state == IWL_TI_CT_KILL) {
                                iwl_perform_ct_kill_task(priv, false);
+                       }
                        IWL_DEBUG_TEMP(priv, "Temperature state changed %u\n",
                                        tt->state);
                        IWL_DEBUG_TEMP(priv, "Power Index change to %u\n",
index 8b4922b..0290b33 100644 (file)
@@ -77,7 +77,8 @@
  * @DATA_PATH_GROUP: data path group, uses command IDs from
  *     &enum iwl_data_path_subcmd_ids
  * @NAN_GROUP: NAN group, uses command IDs from &enum iwl_nan_subcmd_ids
- * @TOF_GROUP: TOF group, uses command IDs from &enum iwl_tof_subcmd_ids
+ * @LOCATION_GROUP: location group, uses command IDs from
+ *     &enum iwl_location_subcmd_ids
  * @PROT_OFFLOAD_GROUP: protocol offload group, uses command IDs from
  *     &enum iwl_prot_offload_subcmd_ids
  * @REGULATORY_AND_NVM_GROUP: regulatory/NVM group, uses command IDs from
@@ -92,7 +93,7 @@ enum iwl_mvm_command_groups {
        PHY_OPS_GROUP = 0x4,
        DATA_PATH_GROUP = 0x5,
        NAN_GROUP = 0x7,
-       TOF_GROUP = 0x8,
+       LOCATION_GROUP = 0x8,
        PROT_OFFLOAD_GROUP = 0xb,
        REGULATORY_AND_NVM_GROUP = 0xc,
        DEBUG_GROUP = 0xf,
@@ -353,16 +354,6 @@ enum iwl_legacy_cmds {
        PHY_DB_CMD = 0x6c,
 
        /**
-        * @TOF_CMD: &struct iwl_tof_config_cmd
-        */
-       TOF_CMD = 0x10,
-
-       /**
-        * @TOF_NOTIFICATION: &struct iwl_tof_gen_resp_cmd
-        */
-       TOF_NOTIFICATION = 0x11,
-
-       /**
         * @POWER_TABLE_CMD: &struct iwl_device_power_cmd
         */
        POWER_TABLE_CMD = 0x77,
@@ -415,7 +406,11 @@ enum iwl_legacy_cmds {
        TX_ANT_CONFIGURATION_CMD = 0x98,
 
        /**
-        * @STATISTICS_CMD: &struct iwl_statistics_cmd
+        * @STATISTICS_CMD:
+        * one of &struct iwl_statistics_cmd,
+        * &struct iwl_notif_statistics_v11,
+        * &struct iwl_notif_statistics_v10,
+        * &struct iwl_notif_statistics
         */
        STATISTICS_CMD = 0x9c,
 
@@ -423,7 +418,7 @@ enum iwl_legacy_cmds {
         * @STATISTICS_NOTIFICATION:
         * one of &struct iwl_notif_statistics_v10,
         * &struct iwl_notif_statistics_v11,
-        * &struct iwl_notif_statistics_cdb
+        * &struct iwl_notif_statistics
         */
        STATISTICS_NOTIFICATION = 0x9d,
 
index 6fae02f..86ea078 100644 (file)
@@ -224,8 +224,18 @@ struct iwl_wowlan_pattern {
 
 #define IWL_WOWLAN_MAX_PATTERNS        20
 
+/**
+ * struct iwl_wowlan_patterns_cmd - WoWLAN wakeup patterns
+ */
 struct iwl_wowlan_patterns_cmd {
+       /**
+        * @n_patterns: number of patterns
+        */
        __le32 n_patterns;
+
+       /**
+        * @patterns: the patterns, array length in @n_patterns
+        */
        struct iwl_wowlan_pattern patterns[];
 } __packed; /* WOWLAN_PATTERN_ARRAY_API_S_VER_1 */
 
index fdc54a5..93c06e6 100644 (file)
@@ -105,6 +105,12 @@ enum iwl_data_path_subcmd_ids {
        HE_AIR_SNIFFER_CONFIG_CMD = 0x13,
 
        /**
+        * @CHEST_COLLECTOR_FILTER_CONFIG_CMD: Configure the CSI
+        *      matrix collection, uses &struct iwl_channel_estimation_cfg
+        */
+       CHEST_COLLECTOR_FILTER_CONFIG_CMD = 0x14,
+
+       /**
         * @RX_NO_DATA_NOTIF: &struct iwl_rx_no_data
         */
        RX_NO_DATA_NOTIF = 0xF5,
@@ -156,4 +162,53 @@ struct iwl_mu_group_mgmt_notif {
        __le32 user_position[4];
 } __packed; /* MU_GROUP_MNG_NTFY_API_S_VER_1 */
 
+enum iwl_channel_estimation_flags {
+       IWL_CHANNEL_ESTIMATION_ENABLE   = BIT(0),
+       IWL_CHANNEL_ESTIMATION_TIMER    = BIT(1),
+       IWL_CHANNEL_ESTIMATION_COUNTER  = BIT(2),
+};
+
+/**
+ * struct iwl_channel_estimation_cfg - channel estimation reporting config
+ */
+struct iwl_channel_estimation_cfg {
+       /**
+        * @flags: flags, see &enum iwl_channel_estimation_flags
+        */
+       __le32 flags;
+       /**
+        * @timer: if enabled via flags, automatically disable after this many
+        *      microseconds
+        */
+       __le32 timer;
+       /**
+        * @count: if enabled via flags, automatically disable after this many
+        *      frames with channel estimation matrix were captured
+        */
+       __le32 count;
+       /**
+        * @rate_n_flags_mask: only try to record the channel estimation matrix
+        *      if the rate_n_flags value for the received frame (let's call
+        *      that rx_rnf) matches the mask/value given here like this:
+        *      (rx_rnf & rate_n_flags_mask) == rate_n_flags_val.
+        */
+       __le32 rate_n_flags_mask;
+       /**
+        * @rate_n_flags_val: see @rate_n_flags_mask
+        */
+       __le32 rate_n_flags_val;
+       /**
+        * @reserved: reserved (for alignment)
+        */
+       __le32 reserved;
+       /**
+        * @frame_types: bitmap of frame types to capture, the received frame's
+        *      subtype|type takes 6 bits in the frame and the corresponding bit
+        *      in this field must be set to 1 to capture channel estimation for
+        *      that frame type. Set to all-ones to enable capturing for all
+        *      frame types.
+        */
+       __le64 frame_types;
+} __packed; /* CHEST_COLLECTOR_FILTER_CMD_API_S_VER_1 */
+
 #endif /* __iwl_fw_api_datapath_h__ */
index dc1fa37..9885849 100644 (file)
@@ -335,29 +335,11 @@ struct iwl_dbg_mem_access_rsp {
        __le32 data[];
 } __packed; /* DEBUG_(U|L)MAC_RD_WR_RSP_API_S_VER_1 */
 
-#define CONT_REC_COMMAND_SIZE  80
-#define ENABLE_CONT_RECORDING  0x15
-#define DISABLE_CONT_RECORDING 0x16
+#define LDBG_CFG_COMMAND_SIZE  80
 #define BUFFER_ALLOCATION      0x27
 #define START_DEBUG_RECORDING  0x29
 #define STOP_DEBUG_RECORDING   0x2A
 
-/*
- * struct iwl_continuous_record_mode - recording mode
- */
-struct iwl_continuous_record_mode {
-       __le16 enable_recording;
-} __packed;
-
-/*
- * struct iwl_continuous_record_cmd - enable/disable continuous recording
- */
-struct iwl_continuous_record_cmd {
-       struct iwl_continuous_record_mode record_mode;
-       u8 pad[CONT_REC_COMMAND_SIZE -
-               sizeof(struct iwl_continuous_record_mode)];
-} __packed;
-
 /* maximum fragments to be allocated per target of allocationId */
 #define IWL_BUFFER_LOCATION_MAX_FRAGS  2
 
@@ -385,4 +367,17 @@ struct iwl_buffer_allocation_cmd {
        struct iwl_fragment_data fragments[IWL_BUFFER_LOCATION_MAX_FRAGS];
 } __packed; /* BUFFER_ALLOCATION_CMD_API_S_VER_1 */
 
+/**
+ * struct iwl_ldbg_config_cmd - LDBG config command
+ * @type: configuration type
+ * @pad: reserved space for type-dependent data
+ */
+struct iwl_ldbg_config_cmd {
+       __le32 type;
+       union {
+               u8 pad[LDBG_CFG_COMMAND_SIZE - sizeof(__le32)];
+               struct iwl_buffer_allocation_cmd buffer_allocation;
+       }; /* LDBG_CFG_BODY_API_U_VER_2 (partially) */
+} __packed; /* LDBG_CFG_CMD_API_S_VER_2 */
+
 #endif /* __iwl_fw_api_debug_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h
new file mode 100644 (file)
index 0000000..6da91ec
--- /dev/null
@@ -0,0 +1,711 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
+ * All rights reserved.
+ *
+ * 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.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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 MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * 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 DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_fw_api_location_h__
+#define __iwl_fw_api_location_h__
+
+/**
+ * enum iwl_location_subcmd_ids - location group command IDs
+ */
+enum iwl_location_subcmd_ids {
+       /**
+        * @TOF_RANGE_REQ_CMD: TOF ranging request,
+        *      uses &struct iwl_tof_range_req_cmd
+        */
+       TOF_RANGE_REQ_CMD = 0x0,
+       /**
+        * @TOF_CONFIG_CMD: TOF configuration, uses &struct iwl_tof_config_cmd
+        */
+       TOF_CONFIG_CMD = 0x1,
+       /**
+        * @TOF_RANGE_ABORT_CMD: abort ongoing ranging, uses
+        *      &struct iwl_tof_range_abort_cmd
+        */
+       TOF_RANGE_ABORT_CMD = 0x2,
+       /**
+        * @TOF_RANGE_REQ_EXT_CMD: TOF extended ranging config,
+        *      uses &struct iwl_tof_range_request_ext_cmd
+        */
+       TOF_RANGE_REQ_EXT_CMD = 0x3,
+       /**
+        * @TOF_RESPONDER_CONFIG_CMD: FTM responder configuration,
+        *      uses &struct iwl_tof_responder_config_cmd
+        */
+       TOF_RESPONDER_CONFIG_CMD = 0x4,
+       /**
+        * @TOF_RESPONDER_DYN_CONFIG_CMD: FTM dynamic configuration,
+        *      uses &struct iwl_tof_responder_dyn_config_cmd
+        */
+       TOF_RESPONDER_DYN_CONFIG_CMD = 0x5,
+       /**
+        * @CSI_HEADER_NOTIFICATION: CSI header
+        */
+       CSI_HEADER_NOTIFICATION = 0xFA,
+       /**
+        * @CSI_CHUNKS_NOTIFICATION: CSI chunk,
+        *      uses &struct iwl_csi_chunk_notification
+        */
+       CSI_CHUNKS_NOTIFICATION = 0xFB,
+       /**
+        * @TOF_LC_NOTIF: used for LCI/civic location, contains just
+        *      the action frame
+        */
+       TOF_LC_NOTIF = 0xFC,
+       /**
+        * @TOF_RESPONDER_STATS: FTM responder statistics notification,
+        *      uses &struct iwl_ftm_responder_stats
+        */
+       TOF_RESPONDER_STATS = 0xFD,
+       /**
+        * @TOF_MCSI_DEBUG_NOTIF: MCSI debug notification, uses
+        *      &struct iwl_tof_mcsi_notif
+        */
+       TOF_MCSI_DEBUG_NOTIF = 0xFE,
+       /**
+        * @TOF_RANGE_RESPONSE_NOTIF: ranging response, using
+        *      &struct iwl_tof_range_rsp_ntfy
+        */
+       TOF_RANGE_RESPONSE_NOTIF = 0xFF,
+};
+
+/**
+ * struct iwl_tof_config_cmd - ToF configuration
+ * @tof_disabled: indicates if ToF is disabled (or not)
+ * @one_sided_disabled: indicates if one-sided is disabled (or not)
+ * @is_debug_mode: indiciates if debug mode is active
+ * @is_buf_required: indicates if channel estimation buffer is required
+ */
+struct iwl_tof_config_cmd {
+       u8 tof_disabled;
+       u8 one_sided_disabled;
+       u8 is_debug_mode;
+       u8 is_buf_required;
+} __packed;
+
+/**
+ * enum iwl_tof_bandwidth - values for iwl_tof_range_req_ap_entry.bandwidth
+ * @IWL_TOF_BW_20_LEGACY: 20 MHz non-HT
+ * @IWL_TOF_BW_20_HT: 20 MHz HT
+ * @IWL_TOF_BW_40: 40 MHz
+ * @IWL_TOF_BW_80: 80 MHz
+ * @IWL_TOF_BW_160: 160 MHz
+ */
+enum iwl_tof_bandwidth {
+       IWL_TOF_BW_20_LEGACY,
+       IWL_TOF_BW_20_HT,
+       IWL_TOF_BW_40,
+       IWL_TOF_BW_80,
+       IWL_TOF_BW_160,
+}; /* LOCAT_BW_TYPE_E */
+
+/*
+ * enum iwl_tof_algo_type - Algorithym type for range measurement request
+ */
+enum iwl_tof_algo_type {
+       IWL_TOF_ALGO_TYPE_MAX_LIKE      = 0,
+       IWL_TOF_ALGO_TYPE_LINEAR_REG    = 1,
+       IWL_TOF_ALGO_TYPE_FFT           = 2,
+
+       /* Keep last */
+       IWL_TOF_ALGO_TYPE_INVALID,
+}; /* ALGO_TYPE_E */
+
+/*
+ * enum iwl_tof_mcsi_ntfy - Enable/Disable MCSI notifications
+ */
+enum iwl_tof_mcsi_enable {
+       IWL_TOF_MCSI_DISABLED = 0,
+       IWL_TOF_MCSI_ENABLED = 1,
+}; /* MCSI_ENABLE_E */
+
+/**
+ * enum iwl_tof_responder_cmd_valid_field - valid fields in the responder cfg
+ * @IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO: channel info is valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_TOA_OFFSET: ToA offset is valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_COMMON_CALIB: common calibration mode is valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_SPECIFIC_CALIB: spefici calibration mode is
+ *     valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_BSSID: BSSID is valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_TX_ANT: TX antenna is valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_ALGO_TYPE: algorithm type is valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_NON_ASAP_SUPPORT: non-ASAP support is valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_STATISTICS_REPORT_SUPPORT: statistics report
+ *     support is valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_MCSI_NOTIF_SUPPORT: MCSI notification support
+ *     is valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_FAST_ALGO_SUPPORT: fast algorithm support
+ *     is valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_RETRY_ON_ALGO_FAIL: retry on algorithm failure
+ *     is valid
+ * @IWL_TOF_RESPONDER_CMD_VALID_STA_ID: station ID is valid
+ */
+enum iwl_tof_responder_cmd_valid_field {
+       IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO = BIT(0),
+       IWL_TOF_RESPONDER_CMD_VALID_TOA_OFFSET = BIT(1),
+       IWL_TOF_RESPONDER_CMD_VALID_COMMON_CALIB = BIT(2),
+       IWL_TOF_RESPONDER_CMD_VALID_SPECIFIC_CALIB = BIT(3),
+       IWL_TOF_RESPONDER_CMD_VALID_BSSID = BIT(4),
+       IWL_TOF_RESPONDER_CMD_VALID_TX_ANT = BIT(5),
+       IWL_TOF_RESPONDER_CMD_VALID_ALGO_TYPE = BIT(6),
+       IWL_TOF_RESPONDER_CMD_VALID_NON_ASAP_SUPPORT = BIT(7),
+       IWL_TOF_RESPONDER_CMD_VALID_STATISTICS_REPORT_SUPPORT = BIT(8),
+       IWL_TOF_RESPONDER_CMD_VALID_MCSI_NOTIF_SUPPORT = BIT(9),
+       IWL_TOF_RESPONDER_CMD_VALID_FAST_ALGO_SUPPORT = BIT(10),
+       IWL_TOF_RESPONDER_CMD_VALID_RETRY_ON_ALGO_FAIL = BIT(11),
+       IWL_TOF_RESPONDER_CMD_VALID_STA_ID = BIT(12),
+};
+
+/**
+ * enum iwl_tof_responder_cfg_flags - responder configuration flags
+ * @IWL_TOF_RESPONDER_FLAGS_NON_ASAP_SUPPORT: non-ASAP support
+ * @IWL_TOF_RESPONDER_FLAGS_REPORT_STATISTICS: report statistics
+ * @IWL_TOF_RESPONDER_FLAGS_REPORT_MCSI: report MCSI
+ * @IWL_TOF_RESPONDER_FLAGS_ALGO_TYPE: algorithm type
+ * @IWL_TOF_RESPONDER_FLAGS_TOA_OFFSET_MODE: ToA offset mode
+ * @IWL_TOF_RESPONDER_FLAGS_COMMON_CALIB_MODE: common calibration mode
+ * @IWL_TOF_RESPONDER_FLAGS_SPECIFIC_CALIB_MODE: specific calibration mode
+ * @IWL_TOF_RESPONDER_FLAGS_FAST_ALGO_SUPPORT: fast algorithm support
+ * @IWL_TOF_RESPONDER_FLAGS_RETRY_ON_ALGO_FAIL: retry on algorithm fail
+ * @IWL_TOF_RESPONDER_FLAGS_FTM_TX_ANT: TX antenna mask
+ */
+enum iwl_tof_responder_cfg_flags {
+       IWL_TOF_RESPONDER_FLAGS_NON_ASAP_SUPPORT = BIT(0),
+       IWL_TOF_RESPONDER_FLAGS_REPORT_STATISTICS = BIT(1),
+       IWL_TOF_RESPONDER_FLAGS_REPORT_MCSI = BIT(2),
+       IWL_TOF_RESPONDER_FLAGS_ALGO_TYPE = BIT(3) | BIT(4) | BIT(5),
+       IWL_TOF_RESPONDER_FLAGS_TOA_OFFSET_MODE = BIT(6),
+       IWL_TOF_RESPONDER_FLAGS_COMMON_CALIB_MODE = BIT(7),
+       IWL_TOF_RESPONDER_FLAGS_SPECIFIC_CALIB_MODE = BIT(8),
+       IWL_TOF_RESPONDER_FLAGS_FAST_ALGO_SUPPORT = BIT(9),
+       IWL_TOF_RESPONDER_FLAGS_RETRY_ON_ALGO_FAIL = BIT(10),
+       IWL_TOF_RESPONDER_FLAGS_FTM_TX_ANT = RATE_MCS_ANT_ABC_MSK,
+};
+
+/**
+ * struct iwl_tof_responder_config_cmd - ToF AP mode (for debug)
+ * @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field
+ * @responder_cfg_flags: &iwl_tof_responder_cfg_flags
+ * @bandwidth: current AP Bandwidth: &enum iwl_tof_bandwidth
+ * @rate: current AP rate
+ * @channel_num: current AP Channel
+ * @ctrl_ch_position: coding of the control channel position relative to
+ *     the center frequency, see iwl_mvm_get_ctrl_pos()
+ * @sta_id: index of the AP STA when in AP mode
+ * @reserved1: reserved
+ * @toa_offset: Artificial addition [pSec] for the ToA - to be used for debug
+ *     purposes, simulating station movement by adding various values
+ *     to this field
+ * @common_calib: XVT: common calibration value
+ * @specific_calib: XVT: specific calibration value
+ * @bssid: Current AP BSSID
+ * @reserved2: reserved
+ */
+struct iwl_tof_responder_config_cmd {
+       __le32 cmd_valid_fields;
+       __le32 responder_cfg_flags;
+       u8 bandwidth;
+       u8 rate;
+       u8 channel_num;
+       u8 ctrl_ch_position;
+       u8 sta_id;
+       u8 reserved1;
+       __le16 toa_offset;
+       __le16 common_calib;
+       __le16 specific_calib;
+       u8 bssid[ETH_ALEN];
+       __le16 reserved2;
+} __packed; /* TOF_RESPONDER_CONFIG_CMD_API_S_VER_6 */
+
+#define IWL_LCI_CIVIC_IE_MAX_SIZE      400
+
+/**
+ * struct iwl_tof_responder_dyn_config_cmd - Dynamic responder settings
+ * @lci_len: The length of the 1st (LCI) part in the @lci_civic buffer
+ * @civic_len: The length of the 2nd (CIVIC) part in the @lci_civic buffer
+ * @lci_civic: The LCI/CIVIC buffer. LCI data (if exists) comes first, then, if
+ *     needed, 0-padding such that the next part is dword-aligned, then CIVIC
+ *     data (if exists) follows, and then 0-padding again to complete a
+ *     4-multiple long buffer.
+ */
+struct iwl_tof_responder_dyn_config_cmd {
+       __le32 lci_len;
+       __le32 civic_len;
+       u8 lci_civic[];
+} __packed; /* TOF_RESPONDER_DYN_CONFIG_CMD_API_S_VER_2 */
+
+/**
+ * struct iwl_tof_range_request_ext_cmd - extended range req for WLS
+ * @tsf_timer_offset_msec: the recommended time offset (mSec) from the AP's TSF
+ * @reserved: reserved
+ * @min_delta_ftm: Minimal time between two consecutive measurements,
+ *                in units of 100us. 0 means no preference by station
+ * @ftm_format_and_bw20M: FTM Channel Spacing/Format for 20MHz: recommended
+ *                     value be sent to the AP
+ * @ftm_format_and_bw40M: FTM Channel Spacing/Format for 40MHz: recommended
+ *                     value to be sent to the AP
+ * @ftm_format_and_bw80M: FTM Channel Spacing/Format for 80MHz: recommended
+ *                     value to be sent to the AP
+ */
+struct iwl_tof_range_req_ext_cmd {
+       __le16 tsf_timer_offset_msec;
+       __le16 reserved;
+       u8 min_delta_ftm;
+       u8 ftm_format_and_bw20M;
+       u8 ftm_format_and_bw40M;
+       u8 ftm_format_and_bw80M;
+} __packed;
+
+/**
+ * enum iwl_tof_location_query - values for query bitmap
+ * @IWL_TOF_LOC_LCI: query LCI
+ * @IWL_TOF_LOC_CIVIC: query civic
+ */
+enum iwl_tof_location_query {
+       IWL_TOF_LOC_LCI = 0x01,
+       IWL_TOF_LOC_CIVIC = 0x02,
+};
+
+ /**
+ * struct iwl_tof_range_req_ap_entry - AP configuration parameters
+ * @channel_num: Current AP Channel
+ * @bandwidth: Current AP Bandwidth. One of iwl_tof_bandwidth.
+ * @tsf_delta_direction: TSF relatively to the subject AP
+ * @ctrl_ch_position: Coding of the control channel position relative to the
+ *     center frequency, see iwl_mvm_get_ctrl_pos().
+ * @bssid: AP's BSSID
+ * @measure_type: Measurement type: 0 - two sided, 1 - One sided
+ * @num_of_bursts: Recommended value to be sent to the AP.  2s Exponent of the
+ *     number of measurement iterations (min 2^0 = 1, max 2^14)
+ * @burst_period: Recommended value to be sent to the AP. Measurement
+ *     periodicity In units of 100ms. ignored if num_of_bursts = 0
+ * @samples_per_burst: 2-sided: the number of FTMs pairs in single Burst (1-31);
+ *     1-sided: how many rts/cts pairs should be used per burst.
+ * @retries_per_sample: Max number of retries that the LMAC should send
+ *     in case of no replies by the AP.
+ * @tsf_delta: TSF Delta in units of microseconds.
+ *     The difference between the AP TSF and the device local clock.
+ * @location_req: Location Request Bit[0] LCI should be sent in the FTMR;
+ *     Bit[1] Civic should be sent in the FTMR
+ * @asap_mode: 0 - non asap mode, 1 - asap mode (not relevant for one sided)
+ * @enable_dyn_ack: Enable Dynamic ACK BW.
+ *     0: Initiator interact with regular AP;
+ *     1: Initiator interact with Responder machine: need to send the
+ *     Initiator Acks with HT 40MHz / 80MHz, since the Responder should
+ *     use it for its ch est measurement (this flag will be set when we
+ *     configure the opposite machine to be Responder).
+ * @rssi: Last received value
+ *     legal values: -128-0 (0x7f). above 0x0 indicating an invalid value.
+ * @algo_type: &enum iwl_tof_algo_type
+ * @notify_mcsi: &enum iwl_tof_mcsi_ntfy.
+ * @reserved: For alignment and future use
+ */
+struct iwl_tof_range_req_ap_entry {
+       u8 channel_num;
+       u8 bandwidth;
+       u8 tsf_delta_direction;
+       u8 ctrl_ch_position;
+       u8 bssid[ETH_ALEN];
+       u8 measure_type;
+       u8 num_of_bursts;
+       __le16 burst_period;
+       u8 samples_per_burst;
+       u8 retries_per_sample;
+       __le32 tsf_delta;
+       u8 location_req;
+       u8 asap_mode;
+       u8 enable_dyn_ack;
+       s8 rssi;
+       u8 algo_type;
+       u8 notify_mcsi;
+       __le16 reserved;
+} __packed; /* LOCATION_RANGE_REQ_AP_ENTRY_CMD_API_S_VER_3 */
+
+/**
+ * enum iwl_tof_response_mode
+ * @IWL_MVM_TOF_RESPONSE_ASAP: report each AP measurement separately as soon as
+ *                            possible (not supported for this release)
+ * @IWL_MVM_TOF_RESPONSE_TIMEOUT: report all AP measurements as a batch upon
+ *                               timeout expiration
+ * @IWL_MVM_TOF_RESPONSE_COMPLETE: report all AP measurements as a batch at the
+ *                                earlier of: measurements completion / timeout
+ *                                expiration.
+ */
+enum iwl_tof_response_mode {
+       IWL_MVM_TOF_RESPONSE_ASAP,
+       IWL_MVM_TOF_RESPONSE_TIMEOUT,
+       IWL_MVM_TOF_RESPONSE_COMPLETE,
+};
+
+/**
+ * enum iwl_tof_initiator_flags
+ *
+ * @IWL_TOF_INITIATOR_FLAGS_FAST_ALGO_DISABLED: disable fast algo, meaning run
+ *     the algo on ant A+B, instead of only one of them.
+ * @IWL_TOF_INITIATOR_FLAGS_RX_CHAIN_SEL_A: open RX antenna A for FTMs RX
+ * @IWL_TOF_INITIATOR_FLAGS_RX_CHAIN_SEL_B: open RX antenna B for FTMs RX
+ * @IWL_TOF_INITIATOR_FLAGS_RX_CHAIN_SEL_C: open RX antenna C for FTMs RX
+ * @IWL_TOF_INITIATOR_FLAGS_TX_CHAIN_SEL_A: use antenna A fo TX ACKs during FTM
+ * @IWL_TOF_INITIATOR_FLAGS_TX_CHAIN_SEL_B: use antenna B fo TX ACKs during FTM
+ * @IWL_TOF_INITIATOR_FLAGS_TX_CHAIN_SEL_C: use antenna C fo TX ACKs during FTM
+ * @IWL_TOF_INITIATOR_FLAGS_MINDELTA_NO_PREF: no preference for minDeltaFTM
+ */
+enum iwl_tof_initiator_flags {
+       IWL_TOF_INITIATOR_FLAGS_FAST_ALGO_DISABLED = BIT(0),
+       IWL_TOF_INITIATOR_FLAGS_RX_CHAIN_SEL_A = BIT(1),
+       IWL_TOF_INITIATOR_FLAGS_RX_CHAIN_SEL_B = BIT(2),
+       IWL_TOF_INITIATOR_FLAGS_RX_CHAIN_SEL_C = BIT(3),
+       IWL_TOF_INITIATOR_FLAGS_TX_CHAIN_SEL_A = BIT(4),
+       IWL_TOF_INITIATOR_FLAGS_TX_CHAIN_SEL_B = BIT(5),
+       IWL_TOF_INITIATOR_FLAGS_TX_CHAIN_SEL_C = BIT(6),
+       IWL_TOF_INITIATOR_FLAGS_MINDELTA_NO_PREF = BIT(7),
+}; /* LOCATION_RANGE_REQ_CMD_API_S_VER_5 */
+
+#define IWL_MVM_TOF_MAX_APS 5
+#define IWL_MVM_TOF_MAX_TWO_SIDED_APS 5
+
+/**
+ * struct iwl_tof_range_req_cmd - start measurement cmd
+ * @initiator_flags: see flags @ iwl_tof_initiator_flags
+ * @request_id: A Token incremented per request. The same Token will be
+ *             sent back in the range response
+ * @initiator: 0- NW initiated,  1 - Client Initiated
+ * @one_sided_los_disable: '0'- run ML-Algo for both ToF/OneSided,
+ *                        '1' - run ML-Algo for ToF only
+ * @req_timeout: Requested timeout of the response in units of 100ms.
+ *          This is equivalent to the session time configured to the
+ *          LMAC in Initiator Request
+ * @report_policy: Supported partially for this release: For current release -
+ *                the range report will be uploaded as a batch when ready or
+ *                when the session is done (successfully / partially).
+ *                one of iwl_tof_response_mode.
+ * @reserved0: reserved
+ * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @macaddr_random: '0' Use default source MAC address (i.e. p2_p),
+ *                 '1' Use MAC Address randomization according to the below
+ * @range_req_bssid: ranging request BSSID
+ * @macaddr_template: MAC address template to use for non-randomized bits
+ * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template.
+ *               Bits set to 1 shall be randomized by the UMAC
+ * @ftm_rx_chains: Rx chain to open to receive Responder's FTMs (XVT)
+ * @ftm_tx_chains: Tx chain to send the ack to the Responder FTM (XVT)
+ * @common_calib: The common calib value to inject to this measurement calc
+ * @specific_calib: The specific calib value to inject to this measurement calc
+ * @ap: per-AP request data
+ */
+struct iwl_tof_range_req_cmd {
+       __le32 initiator_flags;
+       u8 request_id;
+       u8 initiator;
+       u8 one_sided_los_disable;
+       u8 req_timeout;
+       u8 report_policy;
+       u8 reserved0;
+       u8 num_of_ap;
+       u8 macaddr_random;
+       u8 range_req_bssid[ETH_ALEN];
+       u8 macaddr_template[ETH_ALEN];
+       u8 macaddr_mask[ETH_ALEN];
+       u8 ftm_rx_chains;
+       u8 ftm_tx_chains;
+       __le16 common_calib;
+       __le16 specific_calib;
+       struct iwl_tof_range_req_ap_entry ap[IWL_MVM_TOF_MAX_APS];
+} __packed;
+/* LOCATION_RANGE_REQ_CMD_API_S_VER_5 */
+
+/*
+ * enum iwl_tof_range_request_status - status of the sent request
+ * @IWL_TOF_RANGE_REQUEST_STATUS_SUCCESSFUL - FW successfully received the
+ *     request
+ * @IWL_TOF_RANGE_REQUEST_STATUS_BUSY - FW is busy with a previous request, the
+ *     sent request will not be handled
+ */
+enum iwl_tof_range_request_status {
+       IWL_TOF_RANGE_REQUEST_STATUS_SUCCESS,
+       IWL_TOF_RANGE_REQUEST_STATUS_BUSY,
+};
+
+/**
+ * enum iwl_tof_entry_status
+ *
+ * @IWL_TOF_ENTRY_SUCCESS: successful measurement.
+ * @IWL_TOF_ENTRY_GENERAL_FAILURE: General failure.
+ * @IWL_TOF_ENTRY_NO_RESPONSE: Responder didn't reply to the request.
+ * @IWL_TOF_ENTRY_REQUEST_REJECTED: Responder rejected the request.
+ * @IWL_TOF_ENTRY_NOT_SCHEDULED: Time event was scheduled but not called yet.
+ * @IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT: Time event triggered but no
+ *     measurement was completed.
+ * @IWL_TOF_ENTRY_TARGET_DIFF_CH_CANNOT_CHANGE: No range due inability to switch
+ *     from the primary channel.
+ * @IWL_TOF_ENTRY_RANGE_NOT_SUPPORTED: Device doesn't support FTM.
+ * @IWL_TOF_ENTRY_REQUEST_ABORT_UNKNOWN_REASON: Request aborted due to unknown
+ *     reason.
+ * @IWL_TOF_ENTRY_LOCATION_INVALID_T1_T4_TIME_STAMP: Failure due to invalid
+ *     T1/T4.
+ * @IWL_TOF_ENTRY_11MC_PROTOCOL_FAILURE: Failure due to invalid FTM frame
+ *     structure.
+ * @IWL_TOF_ENTRY_REQUEST_CANNOT_SCHED: Request cannot be scheduled.
+ * @IWL_TOF_ENTRY_RESPONDER_CANNOT_COLABORATE: Responder cannot serve the
+ *     initiator for some period, period supplied in @refusal_period.
+ * @IWL_TOF_ENTRY_BAD_REQUEST_ARGS: Bad request arguments.
+ * @IWL_TOF_ENTRY_WIFI_NOT_ENABLED: Wifi not enabled.
+ * @IWL_TOF_ENTRY_RESPONDER_OVERRIDE_PARAMS: Responder override the original
+ *     parameters within the current session.
+ */
+enum iwl_tof_entry_status {
+       IWL_TOF_ENTRY_SUCCESS = 0,
+       IWL_TOF_ENTRY_GENERAL_FAILURE = 1,
+       IWL_TOF_ENTRY_NO_RESPONSE = 2,
+       IWL_TOF_ENTRY_REQUEST_REJECTED = 3,
+       IWL_TOF_ENTRY_NOT_SCHEDULED = 4,
+       IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT = 5,
+       IWL_TOF_ENTRY_TARGET_DIFF_CH_CANNOT_CHANGE = 6,
+       IWL_TOF_ENTRY_RANGE_NOT_SUPPORTED = 7,
+       IWL_TOF_ENTRY_REQUEST_ABORT_UNKNOWN_REASON = 8,
+       IWL_TOF_ENTRY_LOCATION_INVALID_T1_T4_TIME_STAMP = 9,
+       IWL_TOF_ENTRY_11MC_PROTOCOL_FAILURE = 10,
+       IWL_TOF_ENTRY_REQUEST_CANNOT_SCHED = 11,
+       IWL_TOF_ENTRY_RESPONDER_CANNOT_COLABORATE = 12,
+       IWL_TOF_ENTRY_BAD_REQUEST_ARGS = 13,
+       IWL_TOF_ENTRY_WIFI_NOT_ENABLED = 14,
+       IWL_TOF_ENTRY_RESPONDER_OVERRIDE_PARAMS = 15,
+}; /* LOCATION_RANGE_RSP_AP_ENTRY_NTFY_API_S_VER_2 */
+
+/**
+ * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response)
+ * @bssid: BSSID of the AP
+ * @measure_status: current APs measurement status, one of
+ *     &enum iwl_tof_entry_status.
+ * @measure_bw: Current AP Bandwidth: 0  20MHz, 1  40MHz, 2  80MHz
+ * @rtt: The Round Trip Time that took for the last measurement for
+ *     current AP [pSec]
+ * @rtt_variance: The Variance of the RTT values measured for current AP
+ * @rtt_spread: The Difference between the maximum and the minimum RTT
+ *     values measured for current AP in the current session [pSec]
+ * @rssi: RSSI as uploaded in the Channel Estimation notification
+ * @rssi_spread: The Difference between the maximum and the minimum RSSI values
+ *     measured for current AP in the current session
+ * @reserved: reserved
+ * @refusal_period: refusal period in case of
+ *     @IWL_TOF_ENTRY_RESPONDER_CANNOT_COLABORATE [sec]
+ * @range: Measured range [cm]
+ * @range_variance: Measured range variance [cm]
+ * @timestamp: The GP2 Clock [usec] where Channel Estimation notification was
+ *     uploaded by the LMAC
+ * @t2t3_initiator: as calculated from the algo in the initiator
+ * @t1t4_responder: as calculated from the algo in the responder
+ * @common_calib: Calib val that was used in for this AP measurement
+ * @specific_calib: val that was used in for this AP measurement
+ * @papd_calib_output: The result of the tof papd calibration that was injected
+ *     into the algorithm.
+ */
+struct iwl_tof_range_rsp_ap_entry_ntfy {
+       u8 bssid[ETH_ALEN];
+       u8 measure_status;
+       u8 measure_bw;
+       __le32 rtt;
+       __le32 rtt_variance;
+       __le32 rtt_spread;
+       s8 rssi;
+       u8 rssi_spread;
+       u8 reserved;
+       u8 refusal_period;
+       __le32 range;
+       __le32 range_variance;
+       __le32 timestamp;
+       __le32 t2t3_initiator;
+       __le32 t1t4_responder;
+       __le16 common_calib;
+       __le16 specific_calib;
+       __le32 papd_calib_output;
+} __packed; /* LOCATION_RANGE_RSP_AP_ETRY_NTFY_API_S_VER_3 */
+
+/**
+ * enum iwl_tof_response_status - tof response status
+ *
+ * @IWL_TOF_RESPONSE_SUCCESS: successful range.
+ * @IWL_TOF_RESPONSE_TIMEOUT: request aborted due to timeout expiration.
+ *     partial result of ranges done so far is included in the response.
+ * @IWL_TOF_RESPONSE_ABORTED: Measurement aborted by command.
+ * @IWL_TOF_RESPONSE_FAILED: Measurement request command failed.
+ */
+enum iwl_tof_response_status {
+       IWL_TOF_RESPONSE_SUCCESS = 0,
+       IWL_TOF_RESPONSE_TIMEOUT = 1,
+       IWL_TOF_RESPONSE_ABORTED = 4,
+       IWL_TOF_RESPONSE_FAILED  = 5,
+}; /* LOCATION_RNG_RSP_STATUS */
+
+/**
+ * struct iwl_tof_range_rsp_ntfy - ranging response notification
+ * @request_id: A Token ID of the corresponding Range request
+ * @request_status: status of current measurement session, one of
+ *     &enum iwl_tof_response_status.
+ * @last_in_batch: reprot policy (when not all responses are uploaded at once)
+ * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
+ * @ap: per-AP data
+ */
+struct iwl_tof_range_rsp_ntfy {
+       u8 request_id;
+       u8 request_status;
+       u8 last_in_batch;
+       u8 num_of_aps;
+       struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_MVM_TOF_MAX_APS];
+} __packed;
+
+#define IWL_MVM_TOF_MCSI_BUF_SIZE  (245)
+/**
+ * struct iwl_tof_mcsi_notif - used for debug
+ * @token: token ID for the current session
+ * @role: '0' - initiator, '1' - responder
+ * @reserved: reserved
+ * @initiator_bssid: initiator machine
+ * @responder_bssid: responder machine
+ * @mcsi_buffer: debug data
+ */
+struct iwl_tof_mcsi_notif {
+       u8 token;
+       u8 role;
+       __le16 reserved;
+       u8 initiator_bssid[ETH_ALEN];
+       u8 responder_bssid[ETH_ALEN];
+       u8 mcsi_buffer[IWL_MVM_TOF_MCSI_BUF_SIZE * 4];
+} __packed;
+
+/**
+ * struct iwl_tof_range_abort_cmd
+ * @request_id: corresponds to a range request
+ * @reserved: reserved
+ */
+struct iwl_tof_range_abort_cmd {
+       u8 request_id;
+       u8 reserved[3];
+} __packed;
+
+enum ftm_responder_stats_flags {
+       FTM_RESP_STAT_NON_ASAP_STARTED = BIT(0),
+       FTM_RESP_STAT_NON_ASAP_IN_WIN = BIT(1),
+       FTM_RESP_STAT_NON_ASAP_OUT_WIN = BIT(2),
+       FTM_RESP_STAT_TRIGGER_DUP = BIT(3),
+       FTM_RESP_STAT_DUP = BIT(4),
+       FTM_RESP_STAT_DUP_IN_WIN = BIT(5),
+       FTM_RESP_STAT_DUP_OUT_WIN = BIT(6),
+       FTM_RESP_STAT_SCHED_SUCCESS = BIT(7),
+       FTM_RESP_STAT_ASAP_REQ = BIT(8),
+       FTM_RESP_STAT_NON_ASAP_REQ = BIT(9),
+       FTM_RESP_STAT_ASAP_RESP = BIT(10),
+       FTM_RESP_STAT_NON_ASAP_RESP = BIT(11),
+       FTM_RESP_STAT_FAIL_INITIATOR_INACTIVE = BIT(12),
+       FTM_RESP_STAT_FAIL_INITIATOR_OUT_WIN = BIT(13),
+       FTM_RESP_STAT_FAIL_INITIATOR_RETRY_LIM = BIT(14),
+       FTM_RESP_STAT_FAIL_NEXT_SERVED = BIT(15),
+       FTM_RESP_STAT_FAIL_TRIGGER_ERR = BIT(16),
+       FTM_RESP_STAT_FAIL_GC = BIT(17),
+       FTM_RESP_STAT_SUCCESS = BIT(18),
+       FTM_RESP_STAT_INTEL_IE = BIT(19),
+       FTM_RESP_STAT_INITIATOR_ACTIVE = BIT(20),
+       FTM_RESP_STAT_MEASUREMENTS_AVAILABLE = BIT(21),
+       FTM_RESP_STAT_TRIGGER_UNKNOWN = BIT(22),
+       FTM_RESP_STAT_PROCESS_FAIL = BIT(23),
+       FTM_RESP_STAT_ACK = BIT(24),
+       FTM_RESP_STAT_NACK = BIT(25),
+       FTM_RESP_STAT_INVALID_INITIATOR_ID = BIT(26),
+       FTM_RESP_STAT_TIMER_MIN_DELTA = BIT(27),
+       FTM_RESP_STAT_INITIATOR_REMOVED = BIT(28),
+       FTM_RESP_STAT_INITIATOR_ADDED = BIT(29),
+       FTM_RESP_STAT_ERR_LIST_FULL = BIT(30),
+       FTM_RESP_STAT_INITIATOR_SCHED_NOW = BIT(31),
+}; /* RESP_IND_E */
+
+/**
+ * struct iwl_ftm_responder_stats - FTM responder statistics
+ * @addr: initiator address
+ * @success_ftm: number of successful ftm frames
+ * @ftm_per_burst: num of FTM frames that were received
+ * @flags: &enum ftm_responder_stats_flags
+ * @duration: actual duration of FTM
+ * @allocated_duration: time that was allocated for this FTM session
+ * @bw: FTM request bandwidth
+ * @rate: FTM request rate
+ * @reserved: for alingment and future use
+ */
+struct iwl_ftm_responder_stats {
+       u8 addr[ETH_ALEN];
+       u8 success_ftm;
+       u8 ftm_per_burst;
+       __le32 flags;
+       __le32 duration;
+       __le32 allocated_duration;
+       u8 bw;
+       u8 rate;
+       __le16 reserved;
+} __packed; /* TOF_RESPONDER_STATISTICS_NTFY_S_VER_2 */
+
+#define IWL_CSI_CHUNK_CTL_NUM_MASK     0x3
+#define IWL_CSI_CHUNK_CTL_IDX_MASK     0xc
+
+struct iwl_csi_chunk_notification {
+       __le32 token;
+       __le16 seq;
+       __le16 ctl;
+       __le32 size;
+       u8 data[];
+} __packed; /* CSI_CHUNKS_HDR_NTFY_API_S_VER_1 */
+
+#endif /* __iwl_fw_api_location_h__ */
index 45f61c6..b833b80 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,6 +31,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #define PHY_VHT_CTRL_POS_4_ABOVE  (0x7)
 
 /*
+ * struct iwl_fw_channel_info_v1 - channel information
+ *
  * @band: PHY_BAND_*
  * @channel: channel number
  * @width: PHY_[VHT|LEGACY]_CHANNEL_*
  * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_*
  */
-struct iwl_fw_channel_info {
+struct iwl_fw_channel_info_v1 {
        u8 band;
        u8 channel;
        u8 width;
        u8 ctrl_pos;
-} __packed;
+} __packed; /* CHANNEL_CONFIG_API_S_VER_1 */
+
+/*
+ * struct iwl_fw_channel_info - channel information
+ *
+ * @channel: channel number
+ * @band: PHY_BAND_*
+ * @width: PHY_[VHT|LEGACY]_CHANNEL_*
+ * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_*
+ * @reserved: for future use and alignment
+ */
+struct iwl_fw_channel_info {
+       __le32 channel;
+       u8 band;
+       u8 width;
+       u8 ctrl_pos;
+       u8 reserved;
+} __packed; /*CHANNEL_CONFIG_API_S_VER_2 */
 
 #define PHY_RX_CHAIN_DRIVER_FORCE_POS  (0)
 #define PHY_RX_CHAIN_DRIVER_FORCE_MSK \
@@ -134,6 +155,22 @@ struct iwl_fw_channel_info {
 
 /* TODO: complete missing documentation */
 /**
+ * struct iwl_phy_context_cmd_tail - tail of iwl_phy_ctx_cmd for alignment with
+ *     various channel structures.
+ *
+ * @txchain_info: ???
+ * @rxchain_info: ???
+ * @acquisition_data: ???
+ * @dsp_cfg_flags: set to 0
+ */
+struct iwl_phy_context_cmd_tail {
+       __le32 txchain_info;
+       __le32 rxchain_info;
+       __le32 acquisition_data;
+       __le32 dsp_cfg_flags;
+} __packed;
+
+/**
  * struct iwl_phy_context_cmd - config of the PHY context
  * ( PHY_CONTEXT_CMD = 0x8 )
  * @id_and_color: ID and color of the relevant Binding
@@ -142,10 +179,7 @@ struct iwl_fw_channel_info {
  *     other value means apply new params after X usecs
  * @tx_param_color: ???
  * @ci: channel info
- * @txchain_info: ???
- * @rxchain_info: ???
- * @acquisition_data: ???
- * @dsp_cfg_flags: set to 0
+ * @tail: command tail
  */
 struct iwl_phy_context_cmd {
        /* COMMON_INDEX_HDR_API_S_VER_1 */
@@ -155,10 +189,7 @@ struct iwl_phy_context_cmd {
        __le32 apply_time;
        __le32 tx_param_color;
        struct iwl_fw_channel_info ci;
-       __le32 txchain_info;
-       __le32 rxchain_info;
-       __le32 acquisition_data;
-       __le32 dsp_cfg_flags;
+       struct iwl_phy_context_cmd_tail tail;
 } __packed; /* PHY_CONTEXT_CMD_API_VER_1 */
 
 #endif /* __iwl_fw_api_phy_ctxt_h__ */
index 0791a85..6e8224c 100644 (file)
@@ -209,8 +209,6 @@ enum iwl_rx_phy_flags {
  * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors
  * @RX_MPDU_RES_STATUS_STA_ID_MSK: station ID mask
  * @RX_MDPU_RES_STATUS_STA_ID_SHIFT: station ID bit shift
- * @RX_MPDU_RES_STATUS_FILTERING_MSK: filter status
- * @RX_MPDU_RES_STATUS2_FILTERING_MSK: filter status 2
  */
 enum iwl_mvm_rx_status {
        RX_MPDU_RES_STATUS_CRC_OK                       = BIT(0),
@@ -238,8 +236,6 @@ enum iwl_mvm_rx_status {
        RX_MPDU_RES_STATUS_CSUM_OK                      = BIT(17),
        RX_MDPU_RES_STATUS_STA_ID_SHIFT                 = 24,
        RX_MPDU_RES_STATUS_STA_ID_MSK                   = 0x1f << RX_MDPU_RES_STATUS_STA_ID_SHIFT,
-       RX_MPDU_RES_STATUS_FILTERING_MSK                = (0xc00000),
-       RX_MPDU_RES_STATUS2_FILTERING_MSK               = (0xc0000000),
 };
 
 /* 9000 series API */
@@ -337,6 +333,8 @@ enum iwl_rx_mpdu_phy_info {
        IWL_RX_MPDU_PHY_AMPDU           = BIT(5),
        IWL_RX_MPDU_PHY_AMPDU_TOGGLE    = BIT(6),
        IWL_RX_MPDU_PHY_SHORT_PREAMBLE  = BIT(7),
+       /* short preamble is only for CCK, for non-CCK overridden by this */
+       IWL_RX_MPDU_PHY_NCCK_ADDTL_NTFY = BIT(7),
        IWL_RX_MPDU_PHY_TSF_OVERLOAD    = BIT(8),
 };
 
@@ -723,6 +721,9 @@ struct iwl_rx_mpdu_desc {
 #define RX_NO_DATA_FRAME_TIME_POS      0
 #define RX_NO_DATA_FRAME_TIME_MSK      (0xfffff << RX_NO_DATA_FRAME_TIME_POS)
 
+#define RX_NO_DATA_RX_VEC0_HE_NSTS_MSK 0x03800000
+#define RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK        0x38000000
+
 /**
  * struct iwl_rx_no_data - RX no data descriptor
  * @info: 7:0 frame type, 15:8 RX error type
@@ -743,7 +744,7 @@ struct iwl_rx_no_data {
        __le32 fr_time;
        __le32 rate;
        __le32 phy_info[2];
-       __le32 rx_vec[3];
+       __le32 rx_vec[2];
 } __packed; /* RX_NO_DATA_NTFY_API_S_VER_1 */
 
 /**
index 53cb622..3188431 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -29,6 +30,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright (C) 2018 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -363,14 +365,7 @@ struct mvm_statistics_general_v8 {
        u8 reserved[4 - (NUM_MAC_INDEX % 4)];
 } __packed; /* STATISTICS_GENERAL_API_S_VER_8 */
 
-struct mvm_statistics_general_cdb_v9 {
-       struct mvm_statistics_general_common_v19 common;
-       __le32 beacon_counter[NUM_MAC_INDEX_CDB];
-       u8 beacon_average_energy[NUM_MAC_INDEX_CDB];
-       u8 reserved[4 - (NUM_MAC_INDEX_CDB % 4)];
-} __packed; /* STATISTICS_GENERAL_API_S_VER_9 */
-
-struct mvm_statistics_general_cdb {
+struct mvm_statistics_general {
        struct mvm_statistics_general_common common;
        __le32 beacon_counter[MAC_INDEX_AUX];
        u8 beacon_average_energy[MAC_INDEX_AUX];
@@ -435,11 +430,11 @@ struct iwl_notif_statistics_v11 {
        struct mvm_statistics_load_v1 load_stats;
 } __packed; /* STATISTICS_NTFY_API_S_VER_11 */
 
-struct iwl_notif_statistics_cdb {
+struct iwl_notif_statistics {
        __le32 flag;
        struct mvm_statistics_rx rx;
        struct mvm_statistics_tx tx;
-       struct mvm_statistics_general_cdb general;
+       struct mvm_statistics_general general;
        struct mvm_statistics_load load_stats;
 } __packed; /* STATISTICS_NTFY_API_S_VER_13 */
 
index 7c6c246..b089285 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,6 +31,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -111,6 +113,17 @@ struct iwl_tdls_channel_switch_frame {
 } __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */
 
 /**
+ * struct iwl_tdls_channel_switch_cmd_tail - tail of iwl_tdls_channel_switch_cmd
+ *
+ * @timing: timing related data for command
+ * @frame: channel-switch request/response template, depending to switch_type
+ */
+struct iwl_tdls_channel_switch_cmd_tail {
+       struct iwl_tdls_channel_switch_timing timing;
+       struct iwl_tdls_channel_switch_frame frame;
+} __packed;
+
+/**
  * struct iwl_tdls_channel_switch_cmd - TDLS channel switch command
  *
  * The command is sent to initiate a channel switch and also in response to
@@ -119,15 +132,13 @@ struct iwl_tdls_channel_switch_frame {
  * @switch_type: see &enum iwl_tdls_channel_switch_type
  * @peer_sta_id: station id of TDLS peer
  * @ci: channel we switch to
- * @timing: timing related data for command
- * @frame: channel-switch request/response template, depending to switch_type
+ * @tail: command tail
  */
 struct iwl_tdls_channel_switch_cmd {
        u8 switch_type;
        __le32 peer_sta_id;
        struct iwl_fw_channel_info ci;
-       struct iwl_tdls_channel_switch_timing timing;
-       struct iwl_tdls_channel_switch_frame frame;
+       struct iwl_tdls_channel_switch_cmd_tail tail;
 } __packed; /* TDLS_STA_CHANNEL_SWITCH_CMD_API_S_VER_1 */
 
 /**
index f824beb..4621ef9 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,6 +31,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -318,6 +320,25 @@ struct iwl_time_event_notif {
 } __packed; /* MAC_TIME_EVENT_NTFY_API_S_VER_1 */
 
 /*
+ * struct iwl_hs20_roc_req_tail - tail of iwl_hs20_roc_req
+ *
+ * @node_addr: Our MAC Address
+ * @reserved: reserved for alignment
+ * @apply_time: GP2 value to start (should always be the current GP2 value)
+ * @apply_time_max_delay: Maximum apply time delay value in TU. Defines max
+ *     time by which start of the event is allowed to be postponed.
+ * @duration: event duration in TU To calculate event duration:
+ *     timeEventDuration = min(duration, remainingQuota)
+ */
+struct iwl_hs20_roc_req_tail {
+       u8 node_addr[ETH_ALEN];
+       __le16 reserved;
+       __le32 apply_time;
+       __le32 apply_time_max_delay;
+       __le32 duration;
+} __packed;
+
+/*
  * Aux ROC command
  *
  * Command requests the firmware to create a time event for a certain duration
@@ -336,13 +357,6 @@ struct iwl_time_event_notif {
  * @sta_id_and_color: station id and color, resumed during "Remain On Channel"
  *     activity.
  * @channel_info: channel info
- * @node_addr: Our MAC Address
- * @reserved: reserved for alignment
- * @apply_time: GP2 value to start (should always be the current GP2 value)
- * @apply_time_max_delay: Maximum apply time delay value in TU. Defines max
- *     time by which start of the event is allowed to be postponed.
- * @duration: event duration in TU To calculate event duration:
- *     timeEventDuration = min(duration, remainingQuota)
  */
 struct iwl_hs20_roc_req {
        /* COMMON_INDEX_HDR_API_S_VER_1 hdr */
@@ -351,11 +365,7 @@ struct iwl_hs20_roc_req {
        __le32 event_unique_id;
        __le32 sta_id_and_color;
        struct iwl_fw_channel_info channel_info;
-       u8 node_addr[ETH_ALEN];
-       __le16 reserved;
-       __le32 apply_time;
-       __le32 apply_time_max_delay;
-       __le32 duration;
+       struct iwl_hs20_roc_req_tail tail;
 } __packed; /* HOT_SPOT_CMD_API_S_VER_1 */
 
 /*
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tof.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tof.h
deleted file mode 100644 (file)
index 7328a16..0000000
+++ /dev/null
@@ -1,393 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license.  When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <linuxwifi@intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
- * All rights reserved.
- *
- * 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.
- *  * Neither the name Intel Corporation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * 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 MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * 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 DAMAGE.
- *
- *****************************************************************************/
-#ifndef __iwl_fw_api_tof_h__
-#define __iwl_fw_api_tof_h__
-
-/* ToF sub-group command IDs */
-enum iwl_mvm_tof_sub_grp_ids {
-       TOF_RANGE_REQ_CMD = 0x1,
-       TOF_CONFIG_CMD = 0x2,
-       TOF_RANGE_ABORT_CMD = 0x3,
-       TOF_RANGE_REQ_EXT_CMD = 0x4,
-       TOF_RESPONDER_CONFIG_CMD = 0x5,
-       TOF_NW_INITIATED_RES_SEND_CMD = 0x6,
-       TOF_NEIGHBOR_REPORT_REQ_CMD = 0x7,
-       TOF_NEIGHBOR_REPORT_RSP_NOTIF = 0xFC,
-       TOF_NW_INITIATED_REQ_RCVD_NOTIF = 0xFD,
-       TOF_RANGE_RESPONSE_NOTIF = 0xFE,
-       TOF_MCSI_DEBUG_NOTIF = 0xFB,
-};
-
-/**
- * struct iwl_tof_config_cmd - ToF configuration
- * @tof_disabled: 0 enabled, 1 - disabled
- * @one_sided_disabled: 0 enabled, 1 - disabled
- * @is_debug_mode: 1 debug mode, 0 - otherwise
- * @is_buf_required: 1 channel estimation buffer required, 0 - otherwise
- */
-struct iwl_tof_config_cmd {
-       __le32 sub_grp_cmd_id;
-       u8 tof_disabled;
-       u8 one_sided_disabled;
-       u8 is_debug_mode;
-       u8 is_buf_required;
-} __packed;
-
-/**
- * struct iwl_tof_responder_config_cmd - ToF AP mode (for debug)
- * @burst_period: future use: (currently hard coded in the LMAC)
- *               The interval between two sequential bursts.
- * @min_delta_ftm: future use: (currently hard coded in the LMAC)
- *                The minimum delay between two sequential FTM Responses
- *                in the same burst.
- * @burst_duration: future use: (currently hard coded in the LMAC)
- *                The total time for all FTMs handshake in the same burst.
- *                Affect the time events duration in the LMAC.
- * @num_of_burst_exp: future use: (currently hard coded in the LMAC)
- *                The number of bursts for the current ToF request. Affect
- *                the number of events allocations in the current iteration.
- * @get_ch_est: for xVT only, NA for driver
- * @abort_responder: when set to '1' - Responder will terminate its activity
- *                  (all other fields in the command are ignored)
- * @recv_sta_req_params: 1 - Responder will ignore the other Responder's
- *                      params and use the recomended Initiator params.
- *                      0 - otherwise
- * @channel_num: current AP Channel
- * @bandwidth: current AP Bandwidth: 0  20MHz, 1  40MHz, 2  80MHz
- * @rate: current AP rate
- * @ctrl_ch_position: coding of the control channel position relative to
- *     the center frequency:
- *
- *     40 MHz
- *             0 below center, 1 above center
- *
- *     80 MHz
- *             bits [0..1]
- *              * 0  the near 20MHz to the center,
- *              * 1  the far  20MHz to the center
- *             bit[2]
- *              as above 40MHz
- * @ftm_per_burst: FTMs per Burst
- * @ftm_resp_ts_avail: '0' - we don't measure over the Initial FTM Response,
- *               '1' - we measure over the Initial FTM Response
- * @asap_mode: ASAP / Non ASAP mode for the current WLS station
- * @sta_id: index of the AP STA when in AP mode
- * @tsf_timer_offset_msecs: The dictated time offset (mSec) from the AP's TSF
- * @toa_offset: Artificial addition [0.1nsec] for the ToA - to be used for debug
- *             purposes, simulating station movement by adding various values
- *             to this field
- * @bssid: Current AP BSSID
- */
-struct iwl_tof_responder_config_cmd {
-       __le32 sub_grp_cmd_id;
-       __le16 burst_period;
-       u8 min_delta_ftm;
-       u8 burst_duration;
-       u8 num_of_burst_exp;
-       u8 get_ch_est;
-       u8 abort_responder;
-       u8 recv_sta_req_params;
-       u8 channel_num;
-       u8 bandwidth;
-       u8 rate;
-       u8 ctrl_ch_position;
-       u8 ftm_per_burst;
-       u8 ftm_resp_ts_avail;
-       u8 asap_mode;
-       u8 sta_id;
-       __le16 tsf_timer_offset_msecs;
-       __le16 toa_offset;
-       u8 bssid[ETH_ALEN];
-} __packed;
-
-/**
- * struct iwl_tof_range_request_ext_cmd - extended range req for WLS
- * @tsf_timer_offset_msec: the recommended time offset (mSec) from the AP's TSF
- * @reserved: reserved
- * @min_delta_ftm: Minimal time between two consecutive measurements,
- *                in units of 100us. 0 means no preference by station
- * @ftm_format_and_bw20M: FTM Channel Spacing/Format for 20MHz: recommended
- *                     value be sent to the AP
- * @ftm_format_and_bw40M: FTM Channel Spacing/Format for 40MHz: recommended
- *                     value to be sent to the AP
- * @ftm_format_and_bw80M: FTM Channel Spacing/Format for 80MHz: recommended
- *                     value to be sent to the AP
- */
-struct iwl_tof_range_req_ext_cmd {
-       __le32 sub_grp_cmd_id;
-       __le16 tsf_timer_offset_msec;
-       __le16 reserved;
-       u8 min_delta_ftm;
-       u8 ftm_format_and_bw20M;
-       u8 ftm_format_and_bw40M;
-       u8 ftm_format_and_bw80M;
-} __packed;
-
-#define IWL_MVM_TOF_MAX_APS 21
-
-/**
- * struct iwl_tof_range_req_ap_entry - AP configuration parameters
- * @channel_num: Current AP Channel
- * @bandwidth: Current AP Bandwidth: 0  20MHz, 1  40MHz, 2  80MHz
- * @tsf_delta_direction: TSF relatively to the subject AP
- * @ctrl_ch_position: Coding of the control channel position relative to the
- *          center frequency.
- *          40MHz  0 below center, 1 above center
- *          80MHz  bits [0..1]: 0  the near 20MHz to the center,
- *                              1  the far  20MHz to the center
- *                 bit[2]  as above 40MHz
- * @bssid: AP's bss id
- * @measure_type: Measurement type: 0 - two sided, 1 - One sided
- * @num_of_bursts: Recommended value to be sent to the AP.  2s Exponent of the
- *                number of measurement iterations (min 2^0 = 1, max 2^14)
- * @burst_period: Recommended value to be sent to the AP. Measurement
- *               periodicity In units of 100ms. ignored if num_of_bursts = 0
- * @samples_per_burst: 2-sided: the number of FTMs pairs in single Burst (1-31)
- *                    1-sided: how many rts/cts pairs should be used per burst.
- * @retries_per_sample: Max number of retries that the LMAC should send
- *                     in case of no replies by the AP.
- * @tsf_delta: TSF Delta in units of microseconds.
- *            The difference between the AP TSF and the device local clock.
- * @location_req: Location Request Bit[0] LCI should be sent in the FTMR
- *                           Bit[1] Civic should be sent in the FTMR
- * @asap_mode: 0 - non asap mode, 1 - asap mode (not relevant for one sided)
- * @enable_dyn_ack: Enable Dynamic ACK BW.
- *         0  Initiator interact with regular AP
- *         1  Initiator interact with Responder machine: need to send the
- *         Initiator Acks with HT 40MHz / 80MHz, since the Responder should
- *         use it for its ch est measurement (this flag will be set when we
- *         configure the opposite machine to be Responder).
- * @rssi: Last received value
- *       leagal values: -128-0 (0x7f). above 0x0 indicating an invalid value.
- */
-struct iwl_tof_range_req_ap_entry {
-       u8 channel_num;
-       u8 bandwidth;
-       u8 tsf_delta_direction;
-       u8 ctrl_ch_position;
-       u8 bssid[ETH_ALEN];
-       u8 measure_type;
-       u8 num_of_bursts;
-       __le16 burst_period;
-       u8 samples_per_burst;
-       u8 retries_per_sample;
-       __le32 tsf_delta;
-       u8 location_req;
-       u8 asap_mode;
-       u8 enable_dyn_ack;
-       s8 rssi;
-} __packed;
-
-/**
- * enum iwl_tof_response_mode
- * @IWL_MVM_TOF_RESPOSE_ASAP: report each AP measurement separately as soon as
- *                           possible (not supported for this release)
- * @IWL_MVM_TOF_RESPOSE_TIMEOUT: report all AP measurements as a batch upon
- *                              timeout expiration
- * @IWL_MVM_TOF_RESPOSE_COMPLETE: report all AP measurements as a batch at the
- *                               earlier of: measurements completion / timeout
- *                               expiration.
- */
-enum iwl_tof_response_mode {
-       IWL_MVM_TOF_RESPOSE_ASAP = 1,
-       IWL_MVM_TOF_RESPOSE_TIMEOUT,
-       IWL_MVM_TOF_RESPOSE_COMPLETE,
-};
-
-/**
- * struct iwl_tof_range_req_cmd - start measurement cmd
- * @request_id: A Token incremented per request. The same Token will be
- *             sent back in the range response
- * @initiator: 0- NW initiated,  1 - Client Initiated
- * @one_sided_los_disable: '0'- run ML-Algo for both ToF/OneSided,
- *                        '1' - run ML-Algo for ToF only
- * @req_timeout: Requested timeout of the response in units of 100ms.
- *          This is equivalent to the session time configured to the
- *          LMAC in Initiator Request
- * @report_policy: Supported partially for this release: For current release -
- *                the range report will be uploaded as a batch when ready or
- *                when the session is done (successfully / partially).
- *                one of iwl_tof_response_mode.
- * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
- * @macaddr_random: '0' Use default source MAC address (i.e. p2_p),
- *                 '1' Use MAC Address randomization according to the below
- * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template.
- *               Bits set to 1 shall be randomized by the UMAC
- * @ap: per-AP request data
- */
-struct iwl_tof_range_req_cmd {
-       __le32 sub_grp_cmd_id;
-       u8 request_id;
-       u8 initiator;
-       u8 one_sided_los_disable;
-       u8 req_timeout;
-       u8 report_policy;
-       u8 los_det_disable;
-       u8 num_of_ap;
-       u8 macaddr_random;
-       u8 macaddr_template[ETH_ALEN];
-       u8 macaddr_mask[ETH_ALEN];
-       struct iwl_tof_range_req_ap_entry ap[IWL_MVM_TOF_MAX_APS];
-} __packed;
-
-/**
- * struct iwl_tof_gen_resp_cmd - generic ToF response
- */
-struct iwl_tof_gen_resp_cmd {
-       __le32 sub_grp_cmd_id;
-       u8 data[];
-} __packed;
-
-/**
- * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response)
- * @bssid: BSSID of the AP
- * @measure_status: current APs measurement status, one of
- *     &enum iwl_tof_entry_status.
- * @measure_bw: Current AP Bandwidth: 0  20MHz, 1  40MHz, 2  80MHz
- * @rtt: The Round Trip Time that took for the last measurement for
- *      current AP [nSec]
- * @rtt_variance: The Variance of the RTT values measured for current AP
- * @rtt_spread: The Difference between the maximum and the minimum RTT
- *            values measured for current AP in the current session [nsec]
- * @rssi: RSSI as uploaded in the Channel Estimation notification
- * @rssi_spread: The Difference between the maximum and the minimum RSSI values
- *             measured for current AP in the current session
- * @reserved: reserved
- * @range: Measured range [cm]
- * @range_variance: Measured range variance [cm]
- * @timestamp: The GP2 Clock [usec] where Channel Estimation notification was
- *            uploaded by the LMAC
- */
-struct iwl_tof_range_rsp_ap_entry_ntfy {
-       u8 bssid[ETH_ALEN];
-       u8 measure_status;
-       u8 measure_bw;
-       __le32 rtt;
-       __le32 rtt_variance;
-       __le32 rtt_spread;
-       s8 rssi;
-       u8 rssi_spread;
-       __le16 reserved;
-       __le32 range;
-       __le32 range_variance;
-       __le32 timestamp;
-} __packed;
-
-/**
- * struct iwl_tof_range_rsp_ntfy -
- * @request_id: A Token ID of the corresponding Range request
- * @request_status: status of current measurement session
- * @last_in_batch: reprot policy (when not all responses are uploaded at once)
- * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS)
- * @ap: per-AP data
- */
-struct iwl_tof_range_rsp_ntfy {
-       u8 request_id;
-       u8 request_status;
-       u8 last_in_batch;
-       u8 num_of_aps;
-       struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_MVM_TOF_MAX_APS];
-} __packed;
-
-#define IWL_MVM_TOF_MCSI_BUF_SIZE  (245)
-/**
- * struct iwl_tof_mcsi_notif - used for debug
- * @token: token ID for the current session
- * @role: '0' - initiator, '1' - responder
- * @reserved: reserved
- * @initiator_bssid: initiator machine
- * @responder_bssid: responder machine
- * @mcsi_buffer: debug data
- */
-struct iwl_tof_mcsi_notif {
-       u8 token;
-       u8 role;
-       __le16 reserved;
-       u8 initiator_bssid[ETH_ALEN];
-       u8 responder_bssid[ETH_ALEN];
-       u8 mcsi_buffer[IWL_MVM_TOF_MCSI_BUF_SIZE * 4];
-} __packed;
-
-/**
- * struct iwl_tof_neighbor_report_notif
- * @bssid: BSSID of the AP which sent the report
- * @request_token: same token as the corresponding request
- * @status:
- * @report_ie_len: the length of the response frame starting from the Element ID
- * @data: the IEs
- */
-struct iwl_tof_neighbor_report {
-       u8 bssid[ETH_ALEN];
-       u8 request_token;
-       u8 status;
-       __le16 report_ie_len;
-       u8 data[];
-} __packed;
-
-/**
- * struct iwl_tof_range_abort_cmd
- * @request_id: corresponds to a range request
- * @reserved: reserved
- */
-struct iwl_tof_range_abort_cmd {
-       __le32 sub_grp_cmd_id;
-       u8 request_id;
-       u8 reserved[3];
-} __packed;
-
-#endif
index 2a19b17..22efd94 100644 (file)
@@ -469,6 +469,93 @@ static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = {
        { .start = 0x00a02400, .end = 0x00a02758 },
 };
 
+static const struct iwl_prph_range iwl_prph_dump_addr_22000[] = {
+       { .start = 0x00a00000, .end = 0x00a00000 },
+       { .start = 0x00a0000c, .end = 0x00a00024 },
+       { .start = 0x00a0002c, .end = 0x00a00034 },
+       { .start = 0x00a0003c, .end = 0x00a0003c },
+       { .start = 0x00a00410, .end = 0x00a00418 },
+       { .start = 0x00a00420, .end = 0x00a00420 },
+       { .start = 0x00a00428, .end = 0x00a00428 },
+       { .start = 0x00a00430, .end = 0x00a0043c },
+       { .start = 0x00a00444, .end = 0x00a00444 },
+       { .start = 0x00a00840, .end = 0x00a00840 },
+       { .start = 0x00a00850, .end = 0x00a00858 },
+       { .start = 0x00a01004, .end = 0x00a01008 },
+       { .start = 0x00a01010, .end = 0x00a01010 },
+       { .start = 0x00a01018, .end = 0x00a01018 },
+       { .start = 0x00a01024, .end = 0x00a01024 },
+       { .start = 0x00a0102c, .end = 0x00a01034 },
+       { .start = 0x00a0103c, .end = 0x00a01040 },
+       { .start = 0x00a01048, .end = 0x00a01050 },
+       { .start = 0x00a01058, .end = 0x00a01058 },
+       { .start = 0x00a01060, .end = 0x00a01070 },
+       { .start = 0x00a0108c, .end = 0x00a0108c },
+       { .start = 0x00a01c20, .end = 0x00a01c28 },
+       { .start = 0x00a01d10, .end = 0x00a01d10 },
+       { .start = 0x00a01e28, .end = 0x00a01e2c },
+       { .start = 0x00a01e60, .end = 0x00a01e60 },
+       { .start = 0x00a01e80, .end = 0x00a01e80 },
+       { .start = 0x00a01ea0, .end = 0x00a01ea0 },
+       { .start = 0x00a02000, .end = 0x00a0201c },
+       { .start = 0x00a02024, .end = 0x00a02024 },
+       { .start = 0x00a02040, .end = 0x00a02048 },
+       { .start = 0x00a020c0, .end = 0x00a020e0 },
+       { .start = 0x00a02400, .end = 0x00a02404 },
+       { .start = 0x00a0240c, .end = 0x00a02414 },
+       { .start = 0x00a0241c, .end = 0x00a0243c },
+       { .start = 0x00a02448, .end = 0x00a024bc },
+       { .start = 0x00a024c4, .end = 0x00a024cc },
+       { .start = 0x00a02508, .end = 0x00a02508 },
+       { .start = 0x00a02510, .end = 0x00a02514 },
+       { .start = 0x00a0251c, .end = 0x00a0251c },
+       { .start = 0x00a0252c, .end = 0x00a0255c },
+       { .start = 0x00a02564, .end = 0x00a025a0 },
+       { .start = 0x00a025a8, .end = 0x00a025b4 },
+       { .start = 0x00a025c0, .end = 0x00a025c0 },
+       { .start = 0x00a025e8, .end = 0x00a025f4 },
+       { .start = 0x00a02c08, .end = 0x00a02c18 },
+       { .start = 0x00a02c2c, .end = 0x00a02c38 },
+       { .start = 0x00a02c68, .end = 0x00a02c78 },
+       { .start = 0x00a03000, .end = 0x00a03000 },
+       { .start = 0x00a03010, .end = 0x00a03014 },
+       { .start = 0x00a0301c, .end = 0x00a0302c },
+       { .start = 0x00a03034, .end = 0x00a03038 },
+       { .start = 0x00a03040, .end = 0x00a03044 },
+       { .start = 0x00a03060, .end = 0x00a03068 },
+       { .start = 0x00a03070, .end = 0x00a03070 },
+       { .start = 0x00a0307c, .end = 0x00a03084 },
+       { .start = 0x00a0308c, .end = 0x00a03090 },
+       { .start = 0x00a03098, .end = 0x00a03098 },
+       { .start = 0x00a030a0, .end = 0x00a030a0 },
+       { .start = 0x00a030a8, .end = 0x00a030b4 },
+       { .start = 0x00a030bc, .end = 0x00a030c0 },
+       { .start = 0x00a030c8, .end = 0x00a030f4 },
+       { .start = 0x00a03100, .end = 0x00a0312c },
+       { .start = 0x00a03c00, .end = 0x00a03c5c },
+       { .start = 0x00a04400, .end = 0x00a04454 },
+       { .start = 0x00a04460, .end = 0x00a04474 },
+       { .start = 0x00a044c0, .end = 0x00a044ec },
+       { .start = 0x00a04500, .end = 0x00a04504 },
+       { .start = 0x00a04510, .end = 0x00a04538 },
+       { .start = 0x00a04540, .end = 0x00a04548 },
+       { .start = 0x00a04560, .end = 0x00a04560 },
+       { .start = 0x00a04570, .end = 0x00a0457c },
+       { .start = 0x00a04590, .end = 0x00a04590 },
+       { .start = 0x00a04598, .end = 0x00a04598 },
+       { .start = 0x00a045c0, .end = 0x00a045f4 },
+       { .start = 0x00a0c000, .end = 0x00a0c018 },
+       { .start = 0x00a0c020, .end = 0x00a0c028 },
+       { .start = 0x00a0c038, .end = 0x00a0c094 },
+       { .start = 0x00a0c0c0, .end = 0x00a0c104 },
+       { .start = 0x00a0c10c, .end = 0x00a0c118 },
+       { .start = 0x00a0c150, .end = 0x00a0c174 },
+       { .start = 0x00a0c17c, .end = 0x00a0c188 },
+       { .start = 0x00a0c190, .end = 0x00a0c198 },
+       { .start = 0x00a0c1a0, .end = 0x00a0c1a8 },
+       { .start = 0x00a0c1b0, .end = 0x00a0c1b8 },
+};
+
 static void iwl_read_prph_block(struct iwl_trans *trans, u32 start,
                                u32 len_bytes, __le32 *data)
 {
@@ -478,15 +565,20 @@ static void iwl_read_prph_block(struct iwl_trans *trans, u32 start,
                *data++ = cpu_to_le32(iwl_read_prph_no_grab(trans, start + i));
 }
 
-static void iwl_dump_prph(struct iwl_trans *trans,
-                         struct iwl_fw_error_dump_data **data,
+static void iwl_dump_prph(struct iwl_fw_runtime *fwrt,
                          const struct iwl_prph_range *iwl_prph_dump_addr,
-                         u32 range_len)
+                         u32 range_len, void *ptr)
 {
        struct iwl_fw_error_dump_prph *prph;
+       struct iwl_trans *trans = fwrt->trans;
+       struct iwl_fw_error_dump_data **data =
+               (struct iwl_fw_error_dump_data **)ptr;
        unsigned long flags;
        u32 i;
 
+       if (!data)
+               return;
+
        IWL_DEBUG_INFO(trans, "WRT PRPH dump\n");
 
        if (!iwl_trans_grab_nic_access(trans, &flags))
@@ -552,37 +644,47 @@ static struct scatterlist *alloc_sgtable(int size)
        return table;
 }
 
-static int iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt)
+static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt,
+                               const struct iwl_prph_range *iwl_prph_dump_addr,
+                               u32 range_len, void *ptr)
 {
-       u32 prph_len = 0;
-       int i;
+       u32 *prph_len = (u32 *)ptr;
+       int i, num_bytes_in_chunk;
+
+       if (!prph_len)
+               return;
 
-       for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr_comm);
-            i++) {
+       for (i = 0; i < range_len; i++) {
                /* The range includes both boundaries */
-               int num_bytes_in_chunk =
-                       iwl_prph_dump_addr_comm[i].end -
-                       iwl_prph_dump_addr_comm[i].start + 4;
+               num_bytes_in_chunk =
+                       iwl_prph_dump_addr[i].end -
+                       iwl_prph_dump_addr[i].start + 4;
 
-               prph_len += sizeof(struct iwl_fw_error_dump_data) +
+               *prph_len += sizeof(struct iwl_fw_error_dump_data) +
                        sizeof(struct iwl_fw_error_dump_prph) +
                        num_bytes_in_chunk;
        }
+}
 
-       if (fwrt->trans->cfg->mq_rx_supported) {
-               for (i = 0; i <
-                       ARRAY_SIZE(iwl_prph_dump_addr_9000); i++) {
-                       /* The range includes both boundaries */
-                       int num_bytes_in_chunk =
-                               iwl_prph_dump_addr_9000[i].end -
-                               iwl_prph_dump_addr_9000[i].start + 4;
-
-                       prph_len += sizeof(struct iwl_fw_error_dump_data) +
-                               sizeof(struct iwl_fw_error_dump_prph) +
-                               num_bytes_in_chunk;
+static void iwl_fw_prph_handler(struct iwl_fw_runtime *fwrt, void *ptr,
+                               void (*handler)(struct iwl_fw_runtime *,
+                                               const struct iwl_prph_range *,
+                                               u32, void *))
+{
+       u32 range_len;
+
+       if (fwrt->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000) {
+               range_len = ARRAY_SIZE(iwl_prph_dump_addr_22000);
+               handler(fwrt, iwl_prph_dump_addr_22000, range_len, ptr);
+       } else {
+               range_len = ARRAY_SIZE(iwl_prph_dump_addr_comm);
+               handler(fwrt, iwl_prph_dump_addr_comm, range_len, ptr);
+
+               if (fwrt->trans->cfg->mq_rx_supported) {
+                       range_len = ARRAY_SIZE(iwl_prph_dump_addr_9000);
+                       handler(fwrt, iwl_prph_dump_addr_9000, range_len, ptr);
                }
        }
-       return prph_len;
 }
 
 static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt,
@@ -646,6 +748,9 @@ static int iwl_fw_rxf_len(struct iwl_fw_runtime *fwrt,
        ADD_LEN(fifo_len, mem_cfg->rxfifo2_size, hdr_len);
 
        /* Count RXF1 sizes */
+       if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC))
+               mem_cfg->num_lmacs = MAX_NUM_LMAC;
+
        for (i = 0; i < mem_cfg->num_lmacs; i++)
                ADD_LEN(fifo_len, mem_cfg->lmac[i].rxfifo1_size, hdr_len);
 
@@ -664,6 +769,9 @@ static int iwl_fw_txf_len(struct iwl_fw_runtime *fwrt,
                goto dump_internal_txf;
 
        /* Count TXF sizes */
+       if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC))
+               mem_cfg->num_lmacs = MAX_NUM_LMAC;
+
        for (i = 0; i < mem_cfg->num_lmacs; i++) {
                int j;
 
@@ -733,6 +841,8 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        if (!fwrt->trans->cfg->dccm_offset || !fwrt->trans->cfg->dccm_len) {
                const struct fw_img *img;
 
+               if (fwrt->cur_fw_img >= IWL_UCODE_TYPE_MAX)
+                       return NULL;
                img = &fwrt->fw->img[fwrt->cur_fw_img];
                sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
                sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
@@ -747,9 +857,9 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
                fifo_len += iwl_fw_txf_len(fwrt, mem_cfg);
 
                /* Make room for PRPH registers */
-               if (!fwrt->trans->cfg->gen2 &&
-                  iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PRPH))
-                       prph_len += iwl_fw_get_prph_len(fwrt);
+               if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PRPH))
+                       iwl_fw_prph_handler(fwrt, &prph_len,
+                                           iwl_fw_get_prph_len);
 
                if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_7000 &&
                    iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RADIO_REG))
@@ -828,7 +938,13 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
                        sizeof(dump_info->dev_human_readable) - 1);
                strncpy(dump_info->bus_human_readable, fwrt->dev->bus->name,
                        sizeof(dump_info->bus_human_readable) - 1);
-               dump_info->rt_status = cpu_to_le32(fwrt->dump.rt_status);
+               dump_info->num_of_lmacs = fwrt->smem_cfg.num_lmacs;
+               dump_info->lmac_err_id[0] =
+                       cpu_to_le32(fwrt->dump.lmac_err_id[0]);
+               if (fwrt->smem_cfg.num_lmacs > 1)
+                       dump_info->lmac_err_id[1] =
+                               cpu_to_le32(fwrt->dump.lmac_err_id[1]);
+               dump_info->umac_err_id = cpu_to_le32(fwrt->dump.umac_err_id);
 
                dump_data = iwl_fw_error_next_data(dump_data);
        }
@@ -935,16 +1051,8 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        if (iwl_fw_dbg_is_paging_enabled(fwrt))
                iwl_dump_paging(fwrt, &dump_data);
 
-       if (prph_len) {
-               iwl_dump_prph(fwrt->trans, &dump_data,
-                             iwl_prph_dump_addr_comm,
-                             ARRAY_SIZE(iwl_prph_dump_addr_comm));
-
-               if (fwrt->trans->cfg->mq_rx_supported)
-                       iwl_dump_prph(fwrt->trans, &dump_data,
-                                     iwl_prph_dump_addr_9000,
-                                     ARRAY_SIZE(iwl_prph_dump_addr_9000));
-       }
+       if (prph_len)
+               iwl_fw_prph_handler(fwrt, &dump_data, iwl_dump_prph);
 
 out:
        dump_file->file_len = cpu_to_le32(file_len);
@@ -1108,13 +1216,13 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt,
                        iwl_dump_prph_ini(fwrt->trans, data, reg);
                        break;
                case IWL_FW_INI_REGION_DRAM_BUFFER:
-                       *dump_mask |= IWL_FW_ERROR_DUMP_FW_MONITOR;
+                       *dump_mask |= BIT(IWL_FW_ERROR_DUMP_FW_MONITOR);
                        break;
                case IWL_FW_INI_REGION_PAGING:
                        if (iwl_fw_dbg_is_paging_enabled(fwrt))
                                iwl_dump_paging(fwrt, data);
                        else
-                               *dump_mask |= IWL_FW_ERROR_DUMP_PAGING;
+                               *dump_mask |= BIT(IWL_FW_ERROR_DUMP_PAGING);
                        break;
                case IWL_FW_INI_REGION_TXF:
                        iwl_fw_dump_txf(fwrt, data);
@@ -1146,10 +1254,6 @@ _iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt,
 
        if (id == FW_DBG_TRIGGER_FW_ASSERT)
                id = IWL_FW_TRIGGER_ID_FW_ASSERT;
-       else if (id == FW_DBG_TRIGGER_USER)
-               id = IWL_FW_TRIGGER_ID_USER_TRIGGER;
-       else if (id < FW_DBG_TRIGGER_MAX)
-               return NULL;
 
        if (WARN_ON(id >= ARRAY_SIZE(fwrt->dump.active_trigs)))
                return NULL;
@@ -1385,7 +1489,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
        if (WARN_ON(!fwrt->dump.active_trigs[id].active))
                return -EINVAL;
 
-       delay = le32_to_cpu(fwrt->dump.active_trigs[id].conf->ignore_consec);
+       delay = le32_to_cpu(fwrt->dump.active_trigs[id].conf->dump_delay);
        occur = le32_to_cpu(fwrt->dump.active_trigs[id].conf->occurrences);
        if (!occur)
                return 0;
@@ -1570,27 +1674,29 @@ iwl_fw_dbg_buffer_allocation(struct iwl_fw_runtime *fwrt,
                             struct iwl_fw_ini_allocation_tlv *alloc)
 {
        struct iwl_trans *trans = fwrt->trans;
-       struct iwl_continuous_record_cmd cont_rec = {};
-       struct iwl_buffer_allocation_cmd *cmd = (void *)&cont_rec.pad[0];
+       struct iwl_ldbg_config_cmd ldbg_cmd = {
+               .type = cpu_to_le32(BUFFER_ALLOCATION),
+       };
+       struct iwl_buffer_allocation_cmd *cmd = &ldbg_cmd.buffer_allocation;
        struct iwl_host_cmd hcmd = {
                .id = LDBG_CONFIG_CMD,
                .flags = CMD_ASYNC,
-               .data[0] = &cont_rec,
-               .len[0] = sizeof(cont_rec),
+               .data[0] = &ldbg_cmd,
+               .len[0] = sizeof(ldbg_cmd),
        };
        void *virtual_addr = NULL;
        u32 size = le32_to_cpu(alloc->size);
        dma_addr_t phys_addr;
 
-       cont_rec.record_mode.enable_recording = cpu_to_le16(BUFFER_ALLOCATION);
-
        if (!trans->num_blocks &&
            le32_to_cpu(alloc->buffer_location) !=
            IWL_FW_INI_LOCATION_DRAM_PATH)
                return;
 
-       virtual_addr = dma_alloc_coherent(fwrt->trans->dev, size,
-                                         &phys_addr, GFP_KERNEL);
+       virtual_addr =
+               dma_alloc_coherent(fwrt->trans->dev, size, &phys_addr,
+                                  GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO |
+                                  __GFP_COMP);
 
        /* TODO: alloc fragments if needed */
        if (!virtual_addr)
index 6aabbdd..330229d 100644 (file)
@@ -102,7 +102,10 @@ static inline void iwl_fw_free_dump_desc(struct iwl_fw_runtime *fwrt)
        if (fwrt->dump.desc != &iwl_dump_desc_assert)
                kfree(fwrt->dump.desc);
        fwrt->dump.desc = NULL;
-       fwrt->dump.rt_status = 0;
+       fwrt->dump.lmac_err_id[0] = 0;
+       if (fwrt->smem_cfg.num_lmacs > 1)
+               fwrt->dump.lmac_err_id[1] = 0;
+       fwrt->dump.umac_err_id = 0;
 }
 
 void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt);
@@ -266,20 +269,20 @@ _iwl_fw_dbg_trigger_simple_stop(struct iwl_fw_runtime *fwrt,
                                        iwl_fw_dbg_get_trigger((fwrt)->fw,\
                                                               (trig)))
 
-static int iwl_fw_dbg_start_stop_hcmd(struct iwl_fw_runtime *fwrt, bool start)
+static inline int
+iwl_fw_dbg_start_stop_hcmd(struct iwl_fw_runtime *fwrt, bool start)
 {
-       struct iwl_continuous_record_cmd cont_rec = {};
+       struct iwl_ldbg_config_cmd cmd = {
+               .type = start ? cpu_to_le32(START_DEBUG_RECORDING) :
+                               cpu_to_le32(STOP_DEBUG_RECORDING),
+       };
        struct iwl_host_cmd hcmd = {
                .id = LDBG_CONFIG_CMD,
                .flags = CMD_ASYNC,
-               .data[0] = &cont_rec,
-               .len[0] = sizeof(cont_rec),
+               .data[0] = &cmd,
+               .len[0] = sizeof(cmd),
        };
 
-       cont_rec.record_mode.enable_recording = start ?
-               cpu_to_le16(START_DEBUG_RECORDING) :
-               cpu_to_le16(STOP_DEBUG_RECORDING);
-
        return iwl_trans_send_cmd(fwrt->trans, &hcmd);
 }
 
@@ -378,6 +381,7 @@ static inline bool iwl_fw_dbg_is_paging_enabled(struct iwl_fw_runtime *fwrt)
 {
        return iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PAGING) &&
                !fwrt->trans->cfg->gen2 &&
+               fwrt->cur_fw_img < IWL_UCODE_TYPE_MAX &&
                fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size &&
                fwrt->fw_paging_db[0].fw_paging_block;
 }
index 65faecf..c02425a 100644 (file)
@@ -180,6 +180,8 @@ enum iwl_fw_error_dump_family {
        IWL_FW_ERROR_DUMP_FAMILY_8 = 8,
 };
 
+#define MAX_NUM_LMAC 2
+
 /**
  * struct iwl_fw_error_dump_info - info on the device / firmware
  * @device_family: the family of the device (7 / 8)
@@ -187,7 +189,10 @@ enum iwl_fw_error_dump_family {
  * @fw_human_readable: human readable FW version
  * @dev_human_readable: name of the device
  * @bus_human_readable: name of the bus used
- * @rt_status: the error_id/rt_status that that triggered the latest dump
+ * @num_of_lmacs: the number of lmacs
+ * @lmac_err_id: the lmac 0/1 error_id/rt_status that triggered the latest dump
+ *     if the dump collection was not initiated by an assert, the value is 0
+ * @umac_err_id: the umac error_id/rt_status that triggered the latest dump
  *     if the dump collection was not initiated by an assert, the value is 0
  */
 struct iwl_fw_error_dump_info {
@@ -196,7 +201,9 @@ struct iwl_fw_error_dump_info {
        u8 fw_human_readable[FW_VER_HUMAN_READABLE_SZ];
        u8 dev_human_readable[64];
        u8 bus_human_readable[8];
-       __le32 rt_status;
+       u8 num_of_lmacs;
+       __le32 umac_err_id;
+       __le32 lmac_err_id[MAX_NUM_LMAC];
 } __packed;
 
 /**
index 81f557c..e8b00b7 100644 (file)
@@ -303,7 +303,6 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t;
  * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory
  * @IWL_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan.
  * @IWL_UCODE_TLV_CAPA_BEAMFORMER: supports Beamformer
- * @IWL_UCODE_TLV_CAPA_TOF_SUPPORT: supports Time of Flight (802.11mc FTM)
  * @IWL_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality
  * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current
  *     tx power value into TPC Report action frame and Link Measurement Report
@@ -334,6 +333,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t;
  * @IWL_UCODE_TLV_CAPA_TLC_OFFLOAD: firmware implements rate scaling algorithm
  * @IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA: firmware implements quota related
  * @IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2: firmware implements Coex Schema 2
+ * @IWL_UCODE_TLV_CAPA_ULTRA_HB_CHANNELS: firmware supports ultra high band
+ *     (6 GHz).
  * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement
  * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts
  * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT
@@ -357,10 +358,13 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t;
  * @IWL_UCODE_TLV_CAPA_TX_POWER_ACK: reduced TX power API has larger
  *     command size (command version 4) that supports toggling ACK TX
  *     power reduction.
- * @IWL_UCODE_TLV_CAPA_MLME_OFFLOAD: supports MLME offload
  * @IWL_UCODE_TLV_CAPA_D3_DEBUG: supports debug recording during D3
  * @IWL_UCODE_TLV_CAPA_MCC_UPDATE_11AX_SUPPORT: MCC response support 11ax
  *     capability.
+ * @IWL_UCODE_TLV_CAPA_CSI_REPORTING: firmware is capable of being configured
+ *     to report the CSI information with (certain) RX frames
+ *
+ * @IWL_UCODE_TLV_CAPA_MLME_OFFLOAD: supports MLME offload
  *
  * @NUM_IWL_UCODE_TLV_CAPA: number of bits used
  */
@@ -369,7 +373,6 @@ enum iwl_ucode_tlv_capa {
        IWL_UCODE_TLV_CAPA_LAR_SUPPORT                  = (__force iwl_ucode_tlv_capa_t)1,
        IWL_UCODE_TLV_CAPA_UMAC_SCAN                    = (__force iwl_ucode_tlv_capa_t)2,
        IWL_UCODE_TLV_CAPA_BEAMFORMER                   = (__force iwl_ucode_tlv_capa_t)3,
-       IWL_UCODE_TLV_CAPA_TOF_SUPPORT                  = (__force iwl_ucode_tlv_capa_t)5,
        IWL_UCODE_TLV_CAPA_TDLS_SUPPORT                 = (__force iwl_ucode_tlv_capa_t)6,
        IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT    = (__force iwl_ucode_tlv_capa_t)8,
        IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT      = (__force iwl_ucode_tlv_capa_t)9,
@@ -394,6 +397,7 @@ enum iwl_ucode_tlv_capa {
        IWL_UCODE_TLV_CAPA_TLC_OFFLOAD                  = (__force iwl_ucode_tlv_capa_t)43,
        IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA                = (__force iwl_ucode_tlv_capa_t)44,
        IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2                = (__force iwl_ucode_tlv_capa_t)45,
+       IWL_UCODE_TLV_CAPA_ULTRA_HB_CHANNELS            = (__force iwl_ucode_tlv_capa_t)48,
        IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE         = (__force iwl_ucode_tlv_capa_t)64,
        IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS            = (__force iwl_ucode_tlv_capa_t)65,
        IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT             = (__force iwl_ucode_tlv_capa_t)67,
@@ -412,6 +416,8 @@ enum iwl_ucode_tlv_capa {
        IWL_UCODE_TLV_CAPA_D3_DEBUG                     = (__force iwl_ucode_tlv_capa_t)87,
        IWL_UCODE_TLV_CAPA_LED_CMD_SUPPORT              = (__force iwl_ucode_tlv_capa_t)88,
        IWL_UCODE_TLV_CAPA_MCC_UPDATE_11AX_SUPPORT      = (__force iwl_ucode_tlv_capa_t)89,
+       IWL_UCODE_TLV_CAPA_CSI_REPORTING                = (__force iwl_ucode_tlv_capa_t)90,
+
        IWL_UCODE_TLV_CAPA_MLME_OFFLOAD                 = (__force iwl_ucode_tlv_capa_t)96,
 
        NUM_IWL_UCODE_TLV_CAPA
index 4f7090f..a0fcbb2 100644 (file)
@@ -142,7 +142,8 @@ struct iwl_fw_runtime {
                u32 *d3_debug_data;
                struct iwl_fw_ini_active_regs active_regs[IWL_FW_INI_MAX_REGION_ID];
                struct iwl_fw_ini_active_triggers active_trigs[IWL_FW_TRIGGER_ID_NUM];
-               u32 rt_status;
+               u32 lmac_err_id[MAX_NUM_LMAC];
+               u32 umac_err_id;
        } dump;
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        struct {
index 91861a9..ff94253 100644 (file)
@@ -335,10 +335,6 @@ struct iwl_csr_params {
  * @fw_name_pre: Firmware filename prefix. The api version and extension
  *     (.ucode) will be added to filename before loading from disk. The
  *     filename is constructed as fw_name_pre<api>.ucode.
- * @fw_name_pre_b_or_c_step: same as @fw_name_pre, only for b or c steps
- *     (if supported)
- * @fw_name_pre_rf_next_step: same as @fw_name_pre_b_or_c_step, only for rf
- *     next step. Supported only in integrated solutions.
  * @ucode_api_max: Highest version of uCode API supported by driver.
  * @ucode_api_min: Lowest version of uCode API supported by driver.
  * @max_inst_size: The maximal length of the fw inst section (only DVM)
@@ -392,8 +388,6 @@ struct iwl_cfg {
        /* params specific to an individual device within a device family */
        const char *name;
        const char *fw_name_pre;
-       const char *fw_name_pre_b_or_c_step;
-       const char *fw_name_pre_rf_next_step;
        /* params not likely to change within a device family */
        const struct iwl_base_params *base_params;
        /* params likely to change within a device family */
@@ -551,29 +545,40 @@ extern const struct iwl_cfg iwl8275_2ac_cfg;
 extern const struct iwl_cfg iwl4165_2ac_cfg;
 extern const struct iwl_cfg iwl9160_2ac_cfg;
 extern const struct iwl_cfg iwl9260_2ac_cfg;
+extern const struct iwl_cfg iwl9260_2ac_160_cfg;
 extern const struct iwl_cfg iwl9260_killer_2ac_cfg;
 extern const struct iwl_cfg iwl9270_2ac_cfg;
 extern const struct iwl_cfg iwl9460_2ac_cfg;
 extern const struct iwl_cfg iwl9560_2ac_cfg;
+extern const struct iwl_cfg iwl9560_2ac_160_cfg;
 extern const struct iwl_cfg iwl9460_2ac_cfg_soc;
 extern const struct iwl_cfg iwl9461_2ac_cfg_soc;
 extern const struct iwl_cfg iwl9462_2ac_cfg_soc;
 extern const struct iwl_cfg iwl9560_2ac_cfg_soc;
+extern const struct iwl_cfg iwl9560_2ac_160_cfg_soc;
 extern const struct iwl_cfg iwl9560_killer_2ac_cfg_soc;
 extern const struct iwl_cfg iwl9560_killer_s_2ac_cfg_soc;
 extern const struct iwl_cfg iwl9460_2ac_cfg_shared_clk;
 extern const struct iwl_cfg iwl9461_2ac_cfg_shared_clk;
 extern const struct iwl_cfg iwl9462_2ac_cfg_shared_clk;
 extern const struct iwl_cfg iwl9560_2ac_cfg_shared_clk;
+extern const struct iwl_cfg iwl9560_2ac_160_cfg_shared_clk;
 extern const struct iwl_cfg iwl9560_killer_2ac_cfg_shared_clk;
 extern const struct iwl_cfg iwl9560_killer_s_2ac_cfg_shared_clk;
 extern const struct iwl_cfg iwl22000_2ac_cfg_hr;
 extern const struct iwl_cfg iwl22000_2ac_cfg_hr_cdb;
 extern const struct iwl_cfg iwl22000_2ac_cfg_jf;
+extern const struct iwl_cfg iwl22560_2ax_cfg_hr;
 extern const struct iwl_cfg iwl22000_2ax_cfg_hr;
+extern const struct iwl_cfg iwl22260_2ax_cfg;
+extern const struct iwl_cfg killer1650s_2ax_cfg_qu_b0_hr_b0;
+extern const struct iwl_cfg killer1650i_2ax_cfg_qu_b0_hr_b0;
+extern const struct iwl_cfg killer1650x_2ax_cfg;
+extern const struct iwl_cfg killer1650w_2ax_cfg;
 extern const struct iwl_cfg iwl9461_2ac_cfg_qu_b0_jf_b0;
 extern const struct iwl_cfg iwl9462_2ac_cfg_qu_b0_jf_b0;
 extern const struct iwl_cfg iwl9560_2ac_cfg_qu_b0_jf_b0;
+extern const struct iwl_cfg iwl9560_2ac_160_cfg_qu_b0_jf_b0;
 extern const struct iwl_cfg killer1550i_2ac_cfg_qu_b0_jf_b0;
 extern const struct iwl_cfg killer1550s_2ac_cfg_qu_b0_jf_b0;
 extern const struct iwl_cfg iwl22000_2ax_cfg_jf;
index caa5806..42af421 100644 (file)
@@ -325,6 +325,7 @@ enum {
 #define CSR_HW_REV_TYPE_7265D          (0x0000210)
 #define CSR_HW_REV_TYPE_NONE           (0x00001F0)
 #define CSR_HW_REV_TYPE_QNJ            (0x0000360)
+#define CSR_HW_REV_TYPE_QNJ_B0         (0x0000364)
 #define CSR_HW_REV_TYPE_HR_CDB         (0x0000340)
 
 /* RF_ID value */
index bf1be98..2efa1df 100644 (file)
@@ -210,18 +210,15 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
 {
        const struct iwl_cfg *cfg = drv->trans->cfg;
        char tag[8];
-       const char *fw_pre_name;
 
        if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_9000 &&
-           (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_B_STEP ||
-            CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_C_STEP))
-               fw_pre_name = cfg->fw_name_pre_b_or_c_step;
-       else if (drv->trans->cfg->integrated &&
-                CSR_HW_RFID_STEP(drv->trans->hw_rf_id) == SILICON_B_STEP &&
-                cfg->fw_name_pre_rf_next_step)
-               fw_pre_name = cfg->fw_name_pre_rf_next_step;
-       else
-               fw_pre_name = cfg->fw_name_pre;
+           (CSR_HW_REV_STEP(drv->trans->hw_rev) != SILICON_B_STEP &&
+            CSR_HW_REV_STEP(drv->trans->hw_rev) != SILICON_C_STEP)) {
+               IWL_ERR(drv,
+                       "Only HW steps B and C are currently supported (0x%0x)\n",
+                       drv->trans->hw_rev);
+               return -EINVAL;
+       }
 
        if (first) {
                drv->fw_index = cfg->ucode_api_max;
@@ -235,15 +232,13 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
                IWL_ERR(drv, "no suitable firmware found!\n");
 
                if (cfg->ucode_api_min == cfg->ucode_api_max) {
-                       IWL_ERR(drv, "%s%d is required\n", fw_pre_name,
+                       IWL_ERR(drv, "%s%d is required\n", cfg->fw_name_pre,
                                cfg->ucode_api_max);
                } else {
                        IWL_ERR(drv, "minimum version required: %s%d\n",
-                               fw_pre_name,
-                               cfg->ucode_api_min);
+                               cfg->fw_name_pre, cfg->ucode_api_min);
                        IWL_ERR(drv, "maximum version supported: %s%d\n",
-                               fw_pre_name,
-                               cfg->ucode_api_max);
+                               cfg->fw_name_pre, cfg->ucode_api_max);
                }
 
                IWL_ERR(drv,
@@ -252,7 +247,7 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
        }
 
        snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode",
-                fw_pre_name, tag);
+                cfg->fw_name_pre, tag);
 
        IWL_DEBUG_INFO(drv, "attempting to load firmware '%s'\n",
                       drv->firmware_name);
index 4f10914..ffd1e64 100644 (file)
@@ -1,10 +1,13 @@
 /******************************************************************************
  *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
  * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
  *
- * Portions of this file are derived from the ipw3945 project.
- *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
  * published by the Free Software Foundation.
  * more details.
  *
  * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
+ * file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <linuxwifi@intel.com>
  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * 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.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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 MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * 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 DAMAGE.
+ *
  *****************************************************************************/
 #include <linux/delay.h>
 #include <linux/device.h>
index 3808585..61477e5 100644 (file)
@@ -1,8 +1,9 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
  *
- * Portions of this file are derived from the ipw3945 project.
+ * GPL LICENSE SUMMARY
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
  * more details.
  *
  * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
+ * file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <linuxwifi@intel.com>
  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  *
+ * BSD LICENSE
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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 MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * 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 DAMAGE.
+ *
  *****************************************************************************/
-
 #ifndef __iwl_io_h__
 #define __iwl_io_h__
 
index 73b1c46..0cae2ef 100644 (file)
@@ -152,4 +152,22 @@ struct iwl_mod_params {
        bool enable_ini;
 };
 
+static inline bool iwl_enable_rx_ampdu(void)
+{
+       if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG)
+               return false;
+       return true;
+}
+
+static inline bool iwl_enable_tx_ampdu(void)
+{
+       if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG)
+               return false;
+       if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG)
+               return true;
+
+       /* enabled by default */
+       return true;
+}
+
 #endif /* #__iwl_modparams_h__ */
index d9afedc..484ef45 100644 (file)
@@ -569,8 +569,7 @@ static struct ieee80211_sband_iftype_data iwl_he_capa[] = {
                        .has_he = true,
                        .he_cap_elem = {
                                .mac_cap_info[0] =
-                                       IEEE80211_HE_MAC_CAP0_HTC_HE |
-                                       IEEE80211_HE_MAC_CAP0_TWT_RES,
+                                       IEEE80211_HE_MAC_CAP0_HTC_HE,
                                .mac_cap_info[1] =
                                        IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
                                        IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
@@ -1196,14 +1195,12 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
        regd_to_copy = sizeof(struct ieee80211_regdomain) +
                valid_rules * sizeof(struct ieee80211_reg_rule);
 
-       copy_rd = kzalloc(regd_to_copy, GFP_KERNEL);
+       copy_rd = kmemdup(regd, regd_to_copy, GFP_KERNEL);
        if (!copy_rd) {
                copy_rd = ERR_PTR(-ENOMEM);
                goto out;
        }
 
-       memcpy(copy_rd, regd, regd_to_copy);
-
 out:
        kfree(regdb_ptrs);
        kfree(regd);
index 9d89b7d..3aaa5f0 100644 (file)
 
 /* FW monitor */
 #define MON_BUFF_SAMPLE_CTL            (0xa03c00)
-#define MON_BUFF_BASE_ADDR             (0xa03c3c)
+#define MON_BUFF_BASE_ADDR             (0xa03c1c)
 #define MON_BUFF_END_ADDR              (0xa03c40)
 #define MON_BUFF_WRPTR                 (0xa03c44)
 #define MON_BUFF_CYCLE_CNT             (0xa03c48)
 /* FW monitor family 8000 and on */
-#define MON_BUFF_BASE_ADDR_VER2                (0xa03c3c)
+#define MON_BUFF_BASE_ADDR_VER2                (0xa03c1c)
 #define MON_BUFF_END_ADDR_VER2         (0xa03c20)
 #define MON_BUFF_WRPTR_VER2            (0xa03c24)
 #define MON_BUFF_CYCLE_CNT_VER2                (0xa03c28)
index 9ffd219..30cbd98 100644 (file)
@@ -7,7 +7,6 @@ iwlmvm-y += power.o coex.o
 iwlmvm-y += tt.o offloading.o tdls.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
 iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
-iwlmvm-y += tof.o
 iwlmvm-$(CONFIG_PM) += d3.o
 
-ccflags-y += -I$(src)/../
+ccflags-y += -I $(srctree)/$(src)/../
index 01b5338..36ed7d6 100644 (file)
@@ -2125,7 +2125,6 @@ static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
 
        file->private_data = inode->i_private;
 
-       ieee80211_stop_queues(mvm->hw);
        synchronize_net();
 
        mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
@@ -2140,10 +2139,9 @@ static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
        rtnl_unlock();
        if (err > 0)
                err = -EINVAL;
-       if (err) {
-               ieee80211_wake_queues(mvm->hw);
+       if (err)
                return err;
-       }
+
        mvm->d3_test_active = true;
        mvm->keep_vif = NULL;
        return 0;
@@ -2223,8 +2221,6 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
                mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
                iwl_mvm_d3_test_disconn_work_iter, mvm->keep_vif);
 
-       ieee80211_wake_queues(mvm->hw);
-
        return 0;
 }
 
index 33b0af2..2453cea 100644 (file)
@@ -60,7 +60,6 @@
  *
  *****************************************************************************/
 #include "mvm.h"
-#include "fw/api/tof.h"
 #include "debugfs.h"
 
 static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
@@ -523,753 +522,30 @@ static ssize_t iwl_dbgfs_os_device_timediff_read(struct file *file,
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
-static ssize_t iwl_dbgfs_tof_enable_write(struct ieee80211_vif *vif,
-                                         char *buf,
-                                         size_t count, loff_t *ppos)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       u32 value;
-       int ret = -EINVAL;
-       char *data;
-
-       mutex_lock(&mvm->mutex);
-
-       data = iwl_dbgfs_is_match("tof_disabled=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.tof_cfg.tof_disabled = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("one_sided_disabled=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.tof_cfg.one_sided_disabled = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("is_debug_mode=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.tof_cfg.is_debug_mode = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("is_buf=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.tof_cfg.is_buf_required = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("send_tof_cfg=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0 && value) {
-                       ret = iwl_mvm_tof_config_cmd(mvm);
-                       goto out;
-               }
-       }
-
-out:
-       mutex_unlock(&mvm->mutex);
-
-       return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_tof_enable_read(struct file *file,
-                                        char __user *user_buf,
-                                        size_t count, loff_t *ppos)
-{
-       struct ieee80211_vif *vif = file->private_data;
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       char buf[256];
-       int pos = 0;
-       const size_t bufsz = sizeof(buf);
-       struct iwl_tof_config_cmd *cmd;
-
-       cmd = &mvm->tof_data.tof_cfg;
-
-       mutex_lock(&mvm->mutex);
-
-       pos += scnprintf(buf + pos, bufsz - pos, "tof_disabled = %d\n",
-                        cmd->tof_disabled);
-       pos += scnprintf(buf + pos, bufsz - pos, "one_sided_disabled = %d\n",
-                        cmd->one_sided_disabled);
-       pos += scnprintf(buf + pos, bufsz - pos, "is_debug_mode = %d\n",
-                        cmd->is_debug_mode);
-       pos += scnprintf(buf + pos, bufsz - pos, "is_buf_required = %d\n",
-                        cmd->is_buf_required);
-
-       mutex_unlock(&mvm->mutex);
-
-       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_tof_responder_params_write(struct ieee80211_vif *vif,
-                                                   char *buf,
-                                                   size_t count, loff_t *ppos)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       u32 value;
-       int ret = 0;
-       char *data;
-
-       mutex_lock(&mvm->mutex);
-
-       data = iwl_dbgfs_is_match("burst_period=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (!ret)
-                       mvm->tof_data.responder_cfg.burst_period =
-                                                       cpu_to_le16(value);
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("min_delta_ftm=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.min_delta_ftm = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("burst_duration=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.burst_duration = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("num_of_burst_exp=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.num_of_burst_exp = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("abort_responder=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.abort_responder = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("get_ch_est=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.get_ch_est = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("recv_sta_req_params=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.recv_sta_req_params = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("channel_num=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.channel_num = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("bandwidth=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.bandwidth = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("rate=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.rate = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("bssid=", buf);
-       if (data) {
-               u8 *mac = mvm->tof_data.responder_cfg.bssid;
-
-               if (!mac_pton(data, mac)) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-       }
-
-       data = iwl_dbgfs_is_match("tsf_timer_offset_msecs=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.tsf_timer_offset_msecs =
-                                                       cpu_to_le16(value);
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("toa_offset=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.toa_offset =
-                                                       cpu_to_le16(value);
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("center_freq=", buf);
-       if (data) {
-               struct iwl_tof_responder_config_cmd *cmd =
-                       &mvm->tof_data.responder_cfg;
-
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0 && value) {
-                       enum nl80211_band band = (cmd->channel_num <= 14) ?
-                                                  NL80211_BAND_2GHZ :
-                                                  NL80211_BAND_5GHZ;
-                       struct ieee80211_channel chn = {
-                               .band = band,
-                               .center_freq = ieee80211_channel_to_frequency(
-                                       cmd->channel_num, band),
-                               };
-                       struct cfg80211_chan_def chandef = {
-                               .chan =  &chn,
-                               .center_freq1 =
-                                       ieee80211_channel_to_frequency(value,
-                                                                      band),
-                       };
-
-                       cmd->ctrl_ch_position = iwl_mvm_get_ctrl_pos(&chandef);
-               }
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("ftm_per_burst=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.ftm_per_burst = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("ftm_resp_ts_avail=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.ftm_resp_ts_avail = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("asap_mode=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.responder_cfg.asap_mode = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("send_responder_cfg=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0 && value) {
-                       ret = iwl_mvm_tof_responder_cmd(mvm, vif);
-                       goto out;
-               }
-       }
-
-out:
-       mutex_unlock(&mvm->mutex);
-
-       return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_tof_responder_params_read(struct file *file,
-                                                  char __user *user_buf,
-                                                  size_t count, loff_t *ppos)
-{
-       struct ieee80211_vif *vif = file->private_data;
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       char buf[256];
-       int pos = 0;
-       const size_t bufsz = sizeof(buf);
-       struct iwl_tof_responder_config_cmd *cmd;
-
-       cmd = &mvm->tof_data.responder_cfg;
-
-       mutex_lock(&mvm->mutex);
-
-       pos += scnprintf(buf + pos, bufsz - pos, "burst_period = %d\n",
-                        le16_to_cpu(cmd->burst_period));
-       pos += scnprintf(buf + pos, bufsz - pos, "burst_duration = %d\n",
-                        cmd->burst_duration);
-       pos += scnprintf(buf + pos, bufsz - pos, "bandwidth = %d\n",
-                        cmd->bandwidth);
-       pos += scnprintf(buf + pos, bufsz - pos, "channel_num = %d\n",
-                        cmd->channel_num);
-       pos += scnprintf(buf + pos, bufsz - pos, "ctrl_ch_position = 0x%x\n",
-                        cmd->ctrl_ch_position);
-       pos += scnprintf(buf + pos, bufsz - pos, "bssid = %pM\n",
-                        cmd->bssid);
-       pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %d\n",
-                        cmd->min_delta_ftm);
-       pos += scnprintf(buf + pos, bufsz - pos, "num_of_burst_exp = %d\n",
-                        cmd->num_of_burst_exp);
-       pos += scnprintf(buf + pos, bufsz - pos, "rate = %d\n", cmd->rate);
-       pos += scnprintf(buf + pos, bufsz - pos, "abort_responder = %d\n",
-                        cmd->abort_responder);
-       pos += scnprintf(buf + pos, bufsz - pos, "get_ch_est = %d\n",
-                        cmd->get_ch_est);
-       pos += scnprintf(buf + pos, bufsz - pos, "recv_sta_req_params = %d\n",
-                        cmd->recv_sta_req_params);
-       pos += scnprintf(buf + pos, bufsz - pos, "ftm_per_burst = %d\n",
-                        cmd->ftm_per_burst);
-       pos += scnprintf(buf + pos, bufsz - pos, "ftm_resp_ts_avail = %d\n",
-                        cmd->ftm_resp_ts_avail);
-       pos += scnprintf(buf + pos, bufsz - pos, "asap_mode = %d\n",
-                        cmd->asap_mode);
-       pos += scnprintf(buf + pos, bufsz - pos,
-                        "tsf_timer_offset_msecs = %d\n",
-                        le16_to_cpu(cmd->tsf_timer_offset_msecs));
-       pos += scnprintf(buf + pos, bufsz - pos, "toa_offset = %d\n",
-                        le16_to_cpu(cmd->toa_offset));
-
-       mutex_unlock(&mvm->mutex);
-
-       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif,
-                                                char *buf, size_t count,
-                                                loff_t *ppos)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       u32 value;
-       int ret = 0;
-       char *data;
-
-       mutex_lock(&mvm->mutex);
-
-       data = iwl_dbgfs_is_match("request_id=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req.request_id = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("initiator=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req.initiator = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("one_sided_los_disable=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req.one_sided_los_disable = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("req_timeout=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req.req_timeout = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("report_policy=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req.report_policy = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("macaddr_random=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req.macaddr_random = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("num_of_ap=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req.num_of_ap = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("macaddr_template=", buf);
-       if (data) {
-               u8 mac[ETH_ALEN];
-
-               if (!mac_pton(data, mac)) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-               memcpy(mvm->tof_data.range_req.macaddr_template, mac, ETH_ALEN);
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("macaddr_mask=", buf);
-       if (data) {
-               u8 mac[ETH_ALEN];
-
-               if (!mac_pton(data, mac)) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-               memcpy(mvm->tof_data.range_req.macaddr_mask, mac, ETH_ALEN);
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("ap=", buf);
-       if (data) {
-               struct iwl_tof_range_req_ap_entry ap = {};
-               int size = sizeof(struct iwl_tof_range_req_ap_entry);
-               u16 burst_period;
-               u8 *mac = ap.bssid;
-               unsigned int i;
-
-               if (sscanf(data, "%u %hhd %hhd %hhd"
-                          "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx"
-                          "%hhd %hhd %hd"
-                          "%hhd %hhd %d"
-                          "%hhx %hhd %hhd %hhd",
-                          &i, &ap.channel_num, &ap.bandwidth,
-                          &ap.ctrl_ch_position,
-                          mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5,
-                          &ap.measure_type, &ap.num_of_bursts,
-                          &burst_period,
-                          &ap.samples_per_burst, &ap.retries_per_sample,
-                          &ap.tsf_delta, &ap.location_req, &ap.asap_mode,
-                          &ap.enable_dyn_ack, &ap.rssi) != 20) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-               if (i >= IWL_MVM_TOF_MAX_APS) {
-                       IWL_ERR(mvm, "Invalid AP index %d\n", i);
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               ap.burst_period = cpu_to_le16(burst_period);
-
-               memcpy(&mvm->tof_data.range_req.ap[i], &ap, size);
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("send_range_request=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0 && value)
-                       ret = iwl_mvm_tof_range_request_cmd(mvm, vif);
-               goto out;
-       }
-
-       ret = -EINVAL;
-out:
-       mutex_unlock(&mvm->mutex);
-       return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_tof_range_request_read(struct file *file,
-                                               char __user *user_buf,
-                                               size_t count, loff_t *ppos)
-{
-       struct ieee80211_vif *vif = file->private_data;
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       char buf[512];
-       int pos = 0;
-       const size_t bufsz = sizeof(buf);
-       struct iwl_tof_range_req_cmd *cmd;
-       int i;
-
-       cmd = &mvm->tof_data.range_req;
-
-       mutex_lock(&mvm->mutex);
-
-       pos += scnprintf(buf + pos, bufsz - pos, "request_id= %d\n",
-                        cmd->request_id);
-       pos += scnprintf(buf + pos, bufsz - pos, "initiator= %d\n",
-                        cmd->initiator);
-       pos += scnprintf(buf + pos, bufsz - pos, "one_sided_los_disable = %d\n",
-                        cmd->one_sided_los_disable);
-       pos += scnprintf(buf + pos, bufsz - pos, "req_timeout= %d\n",
-                        cmd->req_timeout);
-       pos += scnprintf(buf + pos, bufsz - pos, "report_policy= %d\n",
-                        cmd->report_policy);
-       pos += scnprintf(buf + pos, bufsz - pos, "macaddr_random= %d\n",
-                        cmd->macaddr_random);
-       pos += scnprintf(buf + pos, bufsz - pos, "macaddr_template= %pM\n",
-                        cmd->macaddr_template);
-       pos += scnprintf(buf + pos, bufsz - pos, "macaddr_mask= %pM\n",
-                        cmd->macaddr_mask);
-       pos += scnprintf(buf + pos, bufsz - pos, "num_of_ap= %d\n",
-                        cmd->num_of_ap);
-       for (i = 0; i < cmd->num_of_ap; i++) {
-               struct iwl_tof_range_req_ap_entry *ap = &cmd->ap[i];
-
-               pos += scnprintf(buf + pos, bufsz - pos,
-                               "ap %.2d: channel_num=%hhd bw=%hhd"
-                               " control=%hhd bssid=%pM type=%hhd"
-                               " num_of_bursts=%hhd burst_period=%hd ftm=%hhd"
-                               " retries=%hhd tsf_delta=%d"
-                               " tsf_delta_direction=%hhd location_req=0x%hhx "
-                               " asap=%hhd enable=%hhd rssi=%hhd\n",
-                               i, ap->channel_num, ap->bandwidth,
-                               ap->ctrl_ch_position, ap->bssid,
-                               ap->measure_type, ap->num_of_bursts,
-                               ap->burst_period, ap->samples_per_burst,
-                               ap->retries_per_sample, ap->tsf_delta,
-                               ap->tsf_delta_direction,
-                               ap->location_req, ap->asap_mode,
-                               ap->enable_dyn_ack, ap->rssi);
-       }
-
-       mutex_unlock(&mvm->mutex);
-
-       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_tof_range_req_ext_write(struct ieee80211_vif *vif,
-                                                char *buf,
-                                                size_t count, loff_t *ppos)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       u32 value;
-       int ret = 0;
-       char *data;
-
-       mutex_lock(&mvm->mutex);
-
-       data = iwl_dbgfs_is_match("tsf_timer_offset_msec=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req_ext.tsf_timer_offset_msec =
-                                                       cpu_to_le16(value);
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("min_delta_ftm=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req_ext.min_delta_ftm = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("ftm_format_and_bw20M=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req_ext.ftm_format_and_bw20M =
-                                                                       value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("ftm_format_and_bw40M=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req_ext.ftm_format_and_bw40M =
-                                                                       value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("ftm_format_and_bw80M=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.range_req_ext.ftm_format_and_bw80M =
-                                                                       value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("send_range_req_ext=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0 && value)
-                       ret = iwl_mvm_tof_range_request_ext_cmd(mvm, vif);
-               goto out;
-       }
-
-       ret = -EINVAL;
-out:
-       mutex_unlock(&mvm->mutex);
-       return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_tof_range_req_ext_read(struct file *file,
-                                               char __user *user_buf,
-                                               size_t count, loff_t *ppos)
-{
-       struct ieee80211_vif *vif = file->private_data;
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       char buf[256];
-       int pos = 0;
-       const size_t bufsz = sizeof(buf);
-       struct iwl_tof_range_req_ext_cmd *cmd;
-
-       cmd = &mvm->tof_data.range_req_ext;
-
-       mutex_lock(&mvm->mutex);
-
-       pos += scnprintf(buf + pos, bufsz - pos,
-                        "tsf_timer_offset_msec = %hd\n",
-                        cmd->tsf_timer_offset_msec);
-       pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %hhd\n",
-                        cmd->min_delta_ftm);
-       pos += scnprintf(buf + pos, bufsz - pos,
-                        "ftm_format_and_bw20M = %hhd\n",
-                        cmd->ftm_format_and_bw20M);
-       pos += scnprintf(buf + pos, bufsz - pos,
-                        "ftm_format_and_bw40M = %hhd\n",
-                        cmd->ftm_format_and_bw40M);
-       pos += scnprintf(buf + pos, bufsz - pos,
-                        "ftm_format_and_bw80M = %hhd\n",
-                        cmd->ftm_format_and_bw80M);
-
-       mutex_unlock(&mvm->mutex);
-       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_tof_range_abort_write(struct ieee80211_vif *vif,
-                                              char *buf,
-                                              size_t count, loff_t *ppos)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       u32 value;
-       int abort_id, ret = 0;
-       char *data;
-
-       mutex_lock(&mvm->mutex);
-
-       data = iwl_dbgfs_is_match("abort_id=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0)
-                       mvm->tof_data.last_abort_id = value;
-               goto out;
-       }
-
-       data = iwl_dbgfs_is_match("send_range_abort=", buf);
-       if (data) {
-               ret = kstrtou32(data, 10, &value);
-               if (ret == 0 && value) {
-                       abort_id = mvm->tof_data.last_abort_id;
-                       ret = iwl_mvm_tof_range_abort_cmd(mvm, abort_id);
-                       goto out;
-               }
-       }
-
-out:
-       mutex_unlock(&mvm->mutex);
-       return ret ?: count;
-}
-
-static ssize_t iwl_dbgfs_tof_range_abort_read(struct file *file,
-                                             char __user *user_buf,
-                                             size_t count, loff_t *ppos)
-{
-       struct ieee80211_vif *vif = file->private_data;
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       char buf[32];
-       int pos = 0;
-       const size_t bufsz = sizeof(buf);
-       int last_abort_id;
-
-       mutex_lock(&mvm->mutex);
-       last_abort_id = mvm->tof_data.last_abort_id;
-       mutex_unlock(&mvm->mutex);
-
-       pos += scnprintf(buf + pos, bufsz - pos, "last_abort_id = %d\n",
-                        last_abort_id);
-       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-}
-
-static ssize_t iwl_dbgfs_tof_range_response_read(struct file *file,
-                                                char __user *user_buf,
-                                                size_t count, loff_t *ppos)
+static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
+                                          size_t count, loff_t *ppos)
 {
-       struct ieee80211_vif *vif = file->private_data;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm *mvm = mvmvif->mvm;
-       char *buf;
-       int pos = 0;
-       const size_t bufsz = sizeof(struct iwl_tof_range_rsp_ntfy) + 256;
-       struct iwl_tof_range_rsp_ntfy *cmd;
-       int i, ret;
+       u8 value;
+       int ret;
 
-       buf = kzalloc(bufsz, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
+       ret = kstrtou8(buf, 0, &value);
+       if (ret)
+               return ret;
+       if (value > 1)
+               return -EINVAL;
 
        mutex_lock(&mvm->mutex);
-       cmd = &mvm->tof_data.range_resp;
-
-       pos += scnprintf(buf + pos, bufsz - pos, "request_id = %d\n",
-                        cmd->request_id);
-       pos += scnprintf(buf + pos, bufsz - pos, "status = %d\n",
-                        cmd->request_status);
-       pos += scnprintf(buf + pos, bufsz - pos, "last_in_batch = %d\n",
-                        cmd->last_in_batch);
-       pos += scnprintf(buf + pos, bufsz - pos, "num_of_aps = %d\n",
-                        cmd->num_of_aps);
-       for (i = 0; i < cmd->num_of_aps; i++) {
-               struct iwl_tof_range_rsp_ap_entry_ntfy *ap = &cmd->ap[i];
-
-               pos += scnprintf(buf + pos, bufsz - pos,
-                               "ap %.2d: bssid=%pM status=%hhd bw=%hhd"
-                               " rtt=%d rtt_var=%d rtt_spread=%d"
-                               " rssi=%hhd  rssi_spread=%hhd"
-                               " range=%d range_var=%d"
-                               " time_stamp=%d\n",
-                               i, ap->bssid, ap->measure_status,
-                               ap->measure_bw,
-                               ap->rtt, ap->rtt_variance, ap->rtt_spread,
-                               ap->rssi, ap->rssi_spread, ap->range,
-                               ap->range_variance, ap->timestamp);
-       }
+       iwl_mvm_update_low_latency(mvm, vif, value, LOW_LATENCY_DEBUGFS);
        mutex_unlock(&mvm->mutex);
 
-       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
-       kfree(buf);
-       return ret;
+       return count;
 }
 
-static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
-                                          size_t count, loff_t *ppos)
+static ssize_t
+iwl_dbgfs_low_latency_force_write(struct ieee80211_vif *vif, char *buf,
+                                 size_t count, loff_t *ppos)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm *mvm = mvmvif->mvm;
@@ -1279,13 +555,24 @@ static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
        ret = kstrtou8(buf, 0, &value);
        if (ret)
                return ret;
-       if (value > 1)
+
+       if (value > NUM_LOW_LATENCY_FORCE)
                return -EINVAL;
 
        mutex_lock(&mvm->mutex);
-       iwl_mvm_update_low_latency(mvm, vif, value, LOW_LATENCY_DEBUGFS);
+       if (value == LOW_LATENCY_FORCE_UNSET) {
+               iwl_mvm_update_low_latency(mvm, vif, false,
+                                          LOW_LATENCY_DEBUGFS_FORCE);
+               iwl_mvm_update_low_latency(mvm, vif, false,
+                                          LOW_LATENCY_DEBUGFS_FORCE_ENABLE);
+       } else {
+               iwl_mvm_update_low_latency(mvm, vif,
+                                          value == LOW_LATENCY_FORCE_ON,
+                                          LOW_LATENCY_DEBUGFS_FORCE);
+               iwl_mvm_update_low_latency(mvm, vif, true,
+                                          LOW_LATENCY_DEBUGFS_FORCE_ENABLE);
+       }
        mutex_unlock(&mvm->mutex);
-
        return count;
 }
 
@@ -1295,15 +582,25 @@ static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
 {
        struct ieee80211_vif *vif = file->private_data;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       char buf[30] = {};
+       char format[] = "traffic=%d\ndbgfs=%d\nvcmd=%d\nvif_type=%d\n"
+                       "dbgfs_force_enable=%d\ndbgfs_force=%d\nactual=%d\n";
+
+       /*
+        * all values in format are boolean so the size of format is enough
+        * for holding the result string
+        */
+       char buf[sizeof(format) + 1] = {};
        int len;
 
-       len = scnprintf(buf, sizeof(buf) - 1,
-                       "traffic=%d\ndbgfs=%d\nvcmd=%d\nvif_type=%d\n",
+       len = scnprintf(buf, sizeof(buf) - 1, format,
                        !!(mvmvif->low_latency & LOW_LATENCY_TRAFFIC),
                        !!(mvmvif->low_latency & LOW_LATENCY_DEBUGFS),
                        !!(mvmvif->low_latency & LOW_LATENCY_VCMD),
-                       !!(mvmvif->low_latency & LOW_LATENCY_VIF_TYPE));
+                       !!(mvmvif->low_latency & LOW_LATENCY_VIF_TYPE),
+                       !!(mvmvif->low_latency &
+                          LOW_LATENCY_DEBUGFS_FORCE_ENABLE),
+                       !!(mvmvif->low_latency & LOW_LATENCY_DEBUGFS_FORCE),
+                       !!(mvmvif->low_latency_actual));
        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 
@@ -1456,14 +753,9 @@ MVM_DEBUGFS_READ_FILE_OPS(tx_pwr_lmt);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
+MVM_DEBUGFS_WRITE_FILE_OPS(low_latency_force, 10);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_enable, 32);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_request, 512);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_req_ext, 32);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_abort, 32);
-MVM_DEBUGFS_READ_FILE_OPS(tof_range_response);
-MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_responder_params, 32);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(quota_min, 32);
 MVM_DEBUGFS_READ_FILE_OPS(os_device_timediff);
 
@@ -1497,6 +789,7 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        MVM_DEBUGFS_ADD_FILE_VIF(tx_pwr_lmt, mvmvif->dbgfs_dir, 0400);
        MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, 0400);
        MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir, 0600);
+       MVM_DEBUGFS_ADD_FILE_VIF(low_latency_force, mvmvif->dbgfs_dir, 0600);
        MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir, 0600);
        MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir, 0600);
        MVM_DEBUGFS_ADD_FILE_VIF(quota_min, mvmvif->dbgfs_dir, 0600);
@@ -1506,24 +799,6 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
            mvmvif == mvm->bf_allowed_vif)
                MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, 0600);
 
-       if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT) &&
-           !vif->p2p && (vif->type != NL80211_IFTYPE_P2P_DEVICE)) {
-               if (IWL_MVM_TOF_IS_RESPONDER && vif->type == NL80211_IFTYPE_AP)
-                       MVM_DEBUGFS_ADD_FILE_VIF(tof_responder_params,
-                                                mvmvif->dbgfs_dir, 0600);
-
-               MVM_DEBUGFS_ADD_FILE_VIF(tof_range_request, mvmvif->dbgfs_dir,
-                                        0600);
-               MVM_DEBUGFS_ADD_FILE_VIF(tof_range_req_ext, mvmvif->dbgfs_dir,
-                                        0600);
-               MVM_DEBUGFS_ADD_FILE_VIF(tof_enable, mvmvif->dbgfs_dir,
-                                        0600);
-               MVM_DEBUGFS_ADD_FILE_VIF(tof_range_abort, mvmvif->dbgfs_dir,
-                                        0600);
-               MVM_DEBUGFS_ADD_FILE_VIF(tof_range_response, mvmvif->dbgfs_dir,
-                                        0400);
-       }
-
        /*
         * Create symlink for convenience pointing to interface specific
         * debugfs entries for the driver. For example, under
index 52c361a..e136475 100644 (file)
@@ -69,6 +69,7 @@
 #include "sta.h"
 #include "iwl-io.h"
 #include "debugfs.h"
+#include "iwl-modparams.h"
 #include "fw/error-dump.h"
 
 static ssize_t iwl_dbgfs_ctdp_budget_read(struct file *file,
@@ -1206,47 +1207,6 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file,
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
-/*
- * Enable / Disable continuous recording.
- * Cause the FW to start continuous recording, by sending the relevant hcmd.
- * Enable: input of every integer larger than 0, ENABLE_CONT_RECORDING.
- * Disable: for 0 as input, DISABLE_CONT_RECORDING.
- */
-static ssize_t iwl_dbgfs_cont_recording_write(struct iwl_mvm *mvm,
-                                             char *buf, size_t count,
-                                             loff_t *ppos)
-{
-       struct iwl_trans *trans = mvm->trans;
-       const struct iwl_fw_dbg_dest_tlv_v1 *dest = trans->dbg_dest_tlv;
-       struct iwl_continuous_record_cmd cont_rec = {};
-       int ret, rec_mode;
-
-       if (!iwl_mvm_firmware_running(mvm))
-               return -EIO;
-
-       if (!dest)
-               return -EOPNOTSUPP;
-
-       if (dest->monitor_mode != SMEM_MODE ||
-           trans->cfg->device_family < IWL_DEVICE_FAMILY_8000)
-               return -EOPNOTSUPP;
-
-       ret = kstrtoint(buf, 0, &rec_mode);
-       if (ret)
-               return ret;
-
-       cont_rec.record_mode.enable_recording = rec_mode ?
-               cpu_to_le16(ENABLE_CONT_RECORDING) :
-               cpu_to_le16(DISABLE_CONT_RECORDING);
-
-       mutex_lock(&mvm->mutex);
-       ret = iwl_mvm_send_cmd_pdu(mvm, LDBG_CONFIG_CMD, 0,
-                                  sizeof(cont_rec), &cont_rec);
-       mutex_unlock(&mvm->mutex);
-
-       return ret ?: count;
-}
-
 static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm,
                                           char *buf, size_t count,
                                           loff_t *ppos)
@@ -1722,11 +1682,33 @@ iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf,
        return ret ?: count;
 }
 
+struct iwl_mvm_sniffer_apply {
+       struct iwl_mvm *mvm;
+       u16 aid;
+};
+
+static bool iwl_mvm_sniffer_apply(struct iwl_notif_wait_data *notif_data,
+                                 struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_mvm_sniffer_apply *apply = data;
+
+       apply->mvm->cur_aid = cpu_to_le16(apply->aid);
+
+       return true;
+}
+
 static ssize_t
 iwl_dbgfs_he_sniffer_params_write(struct iwl_mvm *mvm, char *buf,
-                       size_t count, loff_t *ppos)
+                                 size_t count, loff_t *ppos)
 {
+       struct iwl_notification_wait wait;
        struct iwl_he_monitor_cmd he_mon_cmd = {};
+       struct iwl_mvm_sniffer_apply apply = {
+               .mvm = mvm,
+       };
+       u16 wait_cmds[] = {
+               iwl_cmd_id(HE_AIR_SNIFFER_CONFIG_CMD, DATA_PATH_GROUP, 0),
+       };
        u32 aid;
        int ret;
 
@@ -1742,10 +1724,30 @@ iwl_dbgfs_he_sniffer_params_write(struct iwl_mvm *mvm, char *buf,
 
        he_mon_cmd.aid = cpu_to_le16(aid);
 
+       apply.aid = aid;
+
        mutex_lock(&mvm->mutex);
+
+       /*
+        * Use the notification waiter to get our function triggered
+        * in sequence with other RX. This ensures that frames we get
+        * on the RX queue _before_ the new configuration is applied
+        * still have mvm->cur_aid pointing to the old AID, and that
+        * frames on the RX queue _after_ the firmware processed the
+        * new configuration (and sent the response, synchronously)
+        * get mvm->cur_aid correctly set to the new AID.
+        */
+       iwl_init_notification_wait(&mvm->notif_wait, &wait,
+                                  wait_cmds, ARRAY_SIZE(wait_cmds),
+                                  iwl_mvm_sniffer_apply, &apply);
+
        ret = iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(HE_AIR_SNIFFER_CONFIG_CMD,
                                                   DATA_PATH_GROUP, 0), 0,
                                   sizeof(he_mon_cmd), &he_mon_cmd);
+
+       /* no need to really wait, we already did anyway */
+       iwl_remove_notification(&mvm->notif_wait, &wait);
+
        mutex_unlock(&mvm->mutex);
 
        return ret ?: count;
@@ -1800,7 +1802,6 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 64);
-MVM_DEBUGFS_WRITE_FILE_OPS(cont_recording, 8);
 MVM_DEBUGFS_WRITE_FILE_OPS(max_amsdu_len, 8);
 MVM_DEBUGFS_WRITE_FILE_OPS(indirection_tbl,
                           (IWL_RSS_INDIRECTION_TABLE_SIZE * 2));
@@ -2004,7 +2005,6 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, 0200);
        MVM_DEBUGFS_ADD_FILE(max_amsdu_len, mvm->debugfs_dir, 0200);
        MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, 0200);
-       MVM_DEBUGFS_ADD_FILE(cont_recording, mvm->debugfs_dir, 0200);
        MVM_DEBUGFS_ADD_FILE(indirection_tbl, mvm->debugfs_dir, 0200);
        MVM_DEBUGFS_ADD_FILE(inject_packet, mvm->debugfs_dir, 0200);
 #ifdef CONFIG_ACPI
@@ -2071,6 +2071,9 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        if (!debugfs_create_blob("nvm_phy_sku", 0400,
                                 mvm->debugfs_dir, &mvm->nvm_phy_sku_blob))
                goto err;
+       if (!debugfs_create_blob("nvm_reg", S_IRUSR,
+                                mvm->debugfs_dir, &mvm->nvm_reg_blob))
+               goto err;
 
        debugfs_create_file("mem", 0600, dbgfs_dir, mvm, &iwl_dbgfs_mem_ops);
 
index 143c7fc..e3eb812 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,6 +31,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -89,7 +91,7 @@
 #include "fw/api/sf.h"
 #include "fw/api/sta.h"
 #include "fw/api/stats.h"
-#include "fw/api/tof.h"
+#include "fw/api/location.h"
 #include "fw/api/tx.h"
 
 #endif /* __fw_api_h__ */
index 0d6c313..d3dc9d2 100644 (file)
@@ -293,9 +293,9 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
                                         enum iwl_ucode_type ucode_type)
 {
        struct iwl_notification_wait alive_wait;
-       struct iwl_mvm_alive_data alive_data;
+       struct iwl_mvm_alive_data alive_data = {};
        const struct fw_img *fw;
-       int ret, i;
+       int ret;
        enum iwl_ucode_type old_type = mvm->fwrt.cur_fw_img;
        static const u16 alive_cmd[] = { MVM_ALIVE };
 
@@ -373,9 +373,6 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
        mvm->queue_info[IWL_MVM_DQA_CMD_QUEUE].tid_bitmap =
                BIT(IWL_MAX_TID_COUNT + 2);
 
-       for (i = 0; i < IEEE80211_MAX_QUEUES; i++)
-               atomic_set(&mvm->mac80211_queue_stop_count[i], 0);
-
        set_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        iwl_fw_set_dbg_rec_on(&mvm->fwrt);
index 7779951..7cfdd07 100644 (file)
@@ -97,11 +97,6 @@ struct iwl_mvm_mac_iface_iterator_data {
        bool found_vif;
 };
 
-struct iwl_mvm_hw_queues_iface_iterator_data {
-       struct ieee80211_vif *exclude_vif;
-       unsigned long used_hw_queues;
-};
-
 static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
                                    struct ieee80211_vif *vif)
 {
@@ -208,61 +203,6 @@ static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
                data->preferred_tsf = NUM_TSF_IDS;
 }
 
-/*
- * Get the mask of the queues used by the vif
- */
-u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif)
-{
-       u32 qmask = 0, ac;
-
-       if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
-               return BIT(IWL_MVM_OFFCHANNEL_QUEUE);
-
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-               if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
-                       qmask |= BIT(vif->hw_queue[ac]);
-       }
-
-       if (vif->type == NL80211_IFTYPE_AP ||
-           vif->type == NL80211_IFTYPE_ADHOC)
-               qmask |= BIT(vif->cab_queue);
-
-       return qmask;
-}
-
-static void iwl_mvm_iface_hw_queues_iter(void *_data, u8 *mac,
-                                        struct ieee80211_vif *vif)
-{
-       struct iwl_mvm_hw_queues_iface_iterator_data *data = _data;
-
-       /* exclude the given vif */
-       if (vif == data->exclude_vif)
-               return;
-
-       data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif);
-}
-
-unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
-                                        struct ieee80211_vif *exclude_vif)
-{
-       struct iwl_mvm_hw_queues_iface_iterator_data data = {
-               .exclude_vif = exclude_vif,
-               .used_hw_queues =
-                       BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
-                       BIT(mvm->aux_queue) |
-                       BIT(IWL_MVM_DQA_GCAST_QUEUE),
-       };
-
-       lockdep_assert_held(&mvm->mutex);
-
-       /* mark all VIF used hw queues */
-       ieee80211_iterate_active_interfaces_atomic(
-               mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-               iwl_mvm_iface_hw_queues_iter, &data);
-
-       return data.used_hw_queues;
-}
-
 static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
                                       struct ieee80211_vif *vif)
 {
@@ -360,8 +300,6 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
                iwl_mvm_mac_iface_iterator, &data);
 
-       used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, vif);
-
        /*
         * In the case we're getting here during resume, it's similar to
         * firmware restart, and with RESUME_ALL the iterator will find
@@ -416,9 +354,6 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
         * the ones here - no real limit
         */
        queue_limit = IEEE80211_MAX_QUEUES;
-       BUILD_BUG_ON(IEEE80211_MAX_QUEUES >
-                    BITS_PER_BYTE *
-                    sizeof(mvm->hw_queue_to_mac80211[0]));
 
        /*
         * Find available queues, and allocate them to the ACs. When in
@@ -446,9 +381,6 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                 * queue value (when queue is enabled).
                 */
                mvmvif->cab_queue = IWL_MVM_DQA_GCAST_QUEUE;
-               vif->cab_queue = IWL_MVM_DQA_GCAST_QUEUE;
-       } else {
-               vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
        }
 
        mvmvif->bcast_sta.sta_id = IWL_MVM_INVALID_STA;
@@ -462,8 +394,6 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 
 exit_fail:
        memset(mvmvif, 0, sizeof(struct iwl_mvm_vif));
-       memset(vif->hw_queue, IEEE80211_INVAL_HW_QUEUE, sizeof(vif->hw_queue));
-       vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
        return ret;
 }
 
@@ -778,27 +708,9 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
 
        if (vif->bss_conf.assoc && vif->bss_conf.he_support &&
            !iwlwifi_mod_params.disable_11ax) {
-               struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-               u8 sta_id = mvmvif->ap_sta_id;
-
                cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX);
-               if (sta_id != IWL_MVM_INVALID_STA) {
-                       struct ieee80211_sta *sta;
-
-                       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
-                               lockdep_is_held(&mvm->mutex));
-
-                       /*
-                        * TODO: we should check the ext cap IE but it is
-                        * unclear why the spec requires two bits (one in HE
-                        * cap IE, and one in the ext cap IE). In the meantime
-                        * rely on the HE cap IE only.
-                        */
-                       if (sta && (sta->he_cap.he_cap_elem.mac_cap_info[0] &
-                                   IEEE80211_HE_MAC_CAP0_TWT_RES))
-                               ctxt_sta->data_policy |=
-                                       cpu_to_le32(TWT_SUPPORTED);
-               }
+               if (vif->bss_conf.twt_requester)
+                       ctxt_sta->data_policy |= cpu_to_le32(TWT_SUPPORTED);
        }
 
 
@@ -881,8 +793,6 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
 
        iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
 
-       cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
-
        /* Override the filter flags to accept only probe requests */
        cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
 
@@ -1203,7 +1113,7 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
 
        if (!fw_has_api(&mvm->fw->ucode_capa,
                        IWL_UCODE_TLV_API_STA_TYPE))
-               ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue);
+               ctxt_ap->mcast_qid = cpu_to_le32(mvmvif->cab_queue);
 
        /*
         * Only set the beacon time when the MAC is being added, when we
@@ -1435,7 +1345,7 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
        agg_status = iwl_mvm_get_agg_status(mvm, beacon_notify_hdr);
        status = le16_to_cpu(agg_status->status) & TX_STATUS_MSK;
        IWL_DEBUG_RX(mvm,
-                    "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n",
+                    "beacon status %#x retries:%d tsf:0x%016llX gp2:0x%X rate:%d\n",
                     status, beacon_notify_hdr->failure_frame,
                     le64_to_cpu(beacon->tsf),
                     mvm->ap_last_beacon_gp2,
@@ -1472,35 +1382,48 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
        }
 }
 
-static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac,
-                                        struct ieee80211_vif *vif)
+void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
+                                    struct iwl_rx_cmd_buffer *rxb)
 {
-       struct iwl_missed_beacons_notif *missed_beacons = _data;
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_missed_beacons_notif *mb = (void *)pkt->data;
        struct iwl_fw_dbg_trigger_missed_bcon *bcon_trig;
        struct iwl_fw_dbg_trigger_tlv *trigger;
        u32 stop_trig_missed_bcon, stop_trig_missed_bcon_since_rx;
        u32 rx_missed_bcon, rx_missed_bcon_since_rx;
+       struct ieee80211_vif *vif;
+       u32 id = le32_to_cpu(mb->mac_id);
 
-       if (mvmvif->id != (u16)le32_to_cpu(missed_beacons->mac_id))
-               return;
+       IWL_DEBUG_INFO(mvm,
+                      "missed bcn mac_id=%u, consecutive=%u (%u, %u, %u)\n",
+                      le32_to_cpu(mb->mac_id),
+                      le32_to_cpu(mb->consec_missed_beacons),
+                      le32_to_cpu(mb->consec_missed_beacons_since_last_rx),
+                      le32_to_cpu(mb->num_recvd_beacons),
+                      le32_to_cpu(mb->num_expected_beacons));
+
+       rcu_read_lock();
+
+       vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, true);
+       if (!vif)
+               goto out;
 
-       rx_missed_bcon = le32_to_cpu(missed_beacons->consec_missed_beacons);
+       rx_missed_bcon = le32_to_cpu(mb->consec_missed_beacons);
        rx_missed_bcon_since_rx =
-               le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx);
+               le32_to_cpu(mb->consec_missed_beacons_since_last_rx);
        /*
         * TODO: the threshold should be adjusted based on latency conditions,
         * and/or in case of a CS flow on one of the other AP vifs.
         */
-       if (le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx) >
-            IWL_MVM_MISSED_BEACONS_THRESHOLD)
+       if (rx_missed_bcon > IWL_MVM_MISSED_BEACONS_THRESHOLD_LONG)
+               iwl_mvm_connection_loss(mvm, vif, "missed beacons");
+       else if (rx_missed_bcon_since_rx > IWL_MVM_MISSED_BEACONS_THRESHOLD)
                ieee80211_beacon_loss(vif);
 
        trigger = iwl_fw_dbg_trigger_on(&mvm->fwrt, ieee80211_vif_to_wdev(vif),
                                        FW_DBG_TRIGGER_MISSED_BEACONS);
        if (!trigger)
-               return;
+               goto out;
 
        bcon_trig = (void *)trigger->data;
        stop_trig_missed_bcon = le32_to_cpu(bcon_trig->stop_consec_missed_bcon);
@@ -1512,28 +1435,11 @@ static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac,
        if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx ||
            rx_missed_bcon >= stop_trig_missed_bcon)
                iwl_fw_dbg_collect_trig(&mvm->fwrt, trigger, NULL);
-}
-
-void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
-                                    struct iwl_rx_cmd_buffer *rxb)
-{
-       struct iwl_rx_packet *pkt = rxb_addr(rxb);
-       struct iwl_missed_beacons_notif *mb = (void *)pkt->data;
-
-       IWL_DEBUG_INFO(mvm,
-                      "missed bcn mac_id=%u, consecutive=%u (%u, %u, %u)\n",
-                      le32_to_cpu(mb->mac_id),
-                      le32_to_cpu(mb->consec_missed_beacons),
-                      le32_to_cpu(mb->consec_missed_beacons_since_last_rx),
-                      le32_to_cpu(mb->num_recvd_beacons),
-                      le32_to_cpu(mb->num_expected_beacons));
-
-       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
-                                                  IEEE80211_IFACE_ITER_NORMAL,
-                                                  iwl_mvm_beacon_loss_iterator,
-                                                  mb);
 
        iwl_fw_dbg_apply_point(&mvm->fwrt, IWL_FW_INI_APPLY_MISSED_BEACONS);
+
+out:
+       rcu_read_unlock();
 }
 
 void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm,
@@ -1575,16 +1481,29 @@ void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm,
        ieee80211_rx_napi(mvm->hw, NULL, skb, NULL);
 }
 
-static void iwl_mvm_probe_resp_data_iter(void *_data, u8 *mac,
-                                        struct ieee80211_vif *vif)
+void iwl_mvm_probe_resp_data_notif(struct iwl_mvm *mvm,
+                                  struct iwl_rx_cmd_buffer *rxb)
 {
-       struct iwl_probe_resp_data_notif *notif = _data;
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_probe_resp_data_notif *notif = (void *)pkt->data;
        struct iwl_probe_resp_data *old_data, *new_data;
+       int len = iwl_rx_packet_payload_len(pkt);
+       u32 id = le32_to_cpu(notif->mac_id);
+       struct ieee80211_vif *vif;
+       struct iwl_mvm_vif *mvmvif;
 
-       if (mvmvif->id != (u16)le32_to_cpu(notif->mac_id))
+       if (WARN_ON_ONCE(len < sizeof(*notif)))
                return;
 
+       IWL_DEBUG_INFO(mvm, "Probe response data notif: noa %d, csa %d\n",
+                      notif->noa_active, notif->csa_counter);
+
+       vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, false);
+       if (!vif)
+               return;
+
+       mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
        new_data = kzalloc(sizeof(*new_data), GFP_KERNEL);
        if (!new_data)
                return;
@@ -1615,25 +1534,6 @@ static void iwl_mvm_probe_resp_data_iter(void *_data, u8 *mac,
                ieee80211_csa_set_counter(vif, notif->csa_counter);
 }
 
-void iwl_mvm_probe_resp_data_notif(struct iwl_mvm *mvm,
-                                  struct iwl_rx_cmd_buffer *rxb)
-{
-       struct iwl_rx_packet *pkt = rxb_addr(rxb);
-       struct iwl_probe_resp_data_notif *notif = (void *)pkt->data;
-       int len = iwl_rx_packet_payload_len(pkt);
-
-       if (WARN_ON_ONCE(len < sizeof(*notif)))
-               return;
-
-       IWL_DEBUG_INFO(mvm, "Probe response data notif: noa %d, csa %d\n",
-                      notif->noa_active, notif->csa_counter);
-
-       ieee80211_iterate_active_interfaces(mvm->hw,
-                                           IEEE80211_IFACE_ITER_ACTIVE,
-                                           iwl_mvm_probe_resp_data_iter,
-                                           notif);
-}
-
 void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm,
                                      struct iwl_rx_cmd_buffer *rxb)
 {
index 97dc464..c9effd7 100644 (file)
@@ -395,6 +395,21 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
        return ret;
 }
 
+const static u8 he_if_types_ext_capa_sta[] = {
+        [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
+        [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
+        [9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT,
+};
+
+const static struct wiphy_iftype_ext_capab he_iftypes_ext_capa[] = {
+       {
+               .iftype = NL80211_IFTYPE_STATION,
+               .extended_capabilities = he_if_types_ext_capa_sta,
+               .extended_capabilities_mask = he_if_types_ext_capa_sta,
+               .extended_capabilities_len = sizeof(he_if_types_ext_capa_sta),
+       },
+};
+
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 {
        struct ieee80211_hw *hw = mvm->hw;
@@ -410,7 +425,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        ieee80211_hw_set(hw, SIGNAL_DBM);
        ieee80211_hw_set(hw, SPECTRUM_MGMT);
        ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
-       ieee80211_hw_set(hw, QUEUE_CONTROL);
        ieee80211_hw_set(hw, WANT_MONITOR_VIF);
        ieee80211_hw_set(hw, SUPPORTS_PS);
        ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
@@ -424,6 +438,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
        ieee80211_hw_set(hw, DEAUTH_NEED_MGD_TX_PREP);
        ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
+       ieee80211_hw_set(hw, BUFF_MMPDU_TXQ);
+       ieee80211_hw_set(hw, STA_MMPDU_TXQ);
+       ieee80211_hw_set(hw, TX_AMSDU);
+       ieee80211_hw_set(hw, TX_FRAG_LIST);
 
        if (iwl_mvm_has_tlc_offload(mvm)) {
                ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
@@ -469,6 +487,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
        hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
+       hw->max_tx_fragments = mvm->trans->max_skb_frags;
 
        BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 6);
        memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers));
@@ -534,6 +553,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
        hw->chanctx_data_size = sizeof(u16);
+       hw->txq_data_size = sizeof(struct iwl_mvm_txq);
 
        hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_P2P_CLIENT) |
@@ -673,6 +693,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                        NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE);
        }
 
+       if (mvm->nvm_data->sku_cap_11ax_enable &&
+           !iwlwifi_mod_params.disable_11ax) {
+               hw->wiphy->iftype_ext_capab = he_iftypes_ext_capa;
+               hw->wiphy->num_iftype_ext_capab =
+                       ARRAY_SIZE(he_iftypes_ext_capa);
+       }
+
        mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
 
 #ifdef CONFIG_PM_SLEEP
@@ -776,7 +803,6 @@ static bool iwl_mvm_defer_tx(struct iwl_mvm *mvm,
                goto out;
 
        __skb_queue_tail(&mvm->d0i3_tx, skb);
-       ieee80211_stop_queues(mvm->hw);
 
        /* trigger wakeup */
        iwl_mvm_ref(mvm, IWL_MVM_REF_TX);
@@ -796,13 +822,15 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
        struct ieee80211_sta *sta = control->sta;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr = (void *)skb->data;
+       bool offchannel = IEEE80211_SKB_CB(skb)->flags &
+               IEEE80211_TX_CTL_TX_OFFCHAN;
 
        if (iwl_mvm_is_radio_killed(mvm)) {
                IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
                goto drop;
        }
 
-       if (info->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
+       if (offchannel &&
            !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) &&
            !test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
                goto drop;
@@ -815,8 +843,8 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
                sta = NULL;
 
        /* If there is no sta, and it's not offchannel - send through AP */
-       if (info->control.vif->type == NL80211_IFTYPE_STATION &&
-           info->hw_queue != IWL_MVM_OFFCHANNEL_QUEUE && !sta) {
+       if (!sta && info->control.vif->type == NL80211_IFTYPE_STATION &&
+           !offchannel) {
                struct iwl_mvm_vif *mvmvif =
                        iwl_mvm_vif_from_mac80211(info->control.vif);
                u8 ap_sta_id = READ_ONCE(mvmvif->ap_sta_id);
@@ -844,22 +872,95 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
        ieee80211_free_txskb(hw, skb);
 }
 
-static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg)
+void iwl_mvm_mac_itxq_xmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
 {
-       if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG)
-               return false;
-       return true;
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_txq *mvmtxq = iwl_mvm_txq_from_mac80211(txq);
+       struct sk_buff *skb = NULL;
+
+       /*
+        * No need for threads to be pending here, they can leave the first
+        * taker all the work.
+        *
+        * mvmtxq->tx_request logic:
+        *
+        * If 0, no one is currently TXing, set to 1 to indicate current thread
+        * will now start TX and other threads should quit.
+        *
+        * If 1, another thread is currently TXing, set to 2 to indicate to
+        * that thread that there was another request. Since that request may
+        * have raced with the check whether the queue is empty, the TXing
+        * thread should check the queue's status one more time before leaving.
+        * This check is done in order to not leave any TX hanging in the queue
+        * until the next TX invocation (which may not even happen).
+        *
+        * If 2, another thread is currently TXing, and it will already double
+        * check the queue, so do nothing.
+        */
+       if (atomic_fetch_add_unless(&mvmtxq->tx_request, 1, 2))
+               return;
+
+       rcu_read_lock();
+       do {
+               while (likely(!mvmtxq->stopped &&
+                             (mvm->trans->system_pm_mode ==
+                              IWL_PLAT_PM_MODE_DISABLED))) {
+                       skb = ieee80211_tx_dequeue(hw, txq);
+
+                       if (!skb)
+                               break;
+
+                       if (!txq->sta)
+                               iwl_mvm_tx_skb_non_sta(mvm, skb);
+                       else
+                               iwl_mvm_tx_skb(mvm, skb, txq->sta);
+               }
+       } while (atomic_dec_return(&mvmtxq->tx_request));
+       rcu_read_unlock();
 }
 
-static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg)
+static void iwl_mvm_mac_wake_tx_queue(struct ieee80211_hw *hw,
+                                     struct ieee80211_txq *txq)
 {
-       if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG)
-               return false;
-       if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG)
-               return true;
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_txq *mvmtxq = iwl_mvm_txq_from_mac80211(txq);
 
-       /* enabled by default */
-       return true;
+       /*
+        * Please note that racing is handled very carefully here:
+        * mvmtxq->txq_id is updated during allocation, and mvmtxq->list is
+        * deleted afterwards.
+        * This means that if:
+        * mvmtxq->txq_id != INVALID_QUEUE && list_empty(&mvmtxq->list):
+        *      queue is allocated and we can TX.
+        * mvmtxq->txq_id != INVALID_QUEUE && !list_empty(&mvmtxq->list):
+        *      a race, should defer the frame.
+        * mvmtxq->txq_id == INVALID_QUEUE && list_empty(&mvmtxq->list):
+        *      need to allocate the queue and defer the frame.
+        * mvmtxq->txq_id == INVALID_QUEUE && !list_empty(&mvmtxq->list):
+        *      queue is already scheduled for allocation, no need to allocate,
+        *      should defer the frame.
+        */
+
+       /* If the queue is allocated TX and return. */
+       if (!txq->sta || mvmtxq->txq_id != IWL_MVM_INVALID_QUEUE) {
+               /*
+                * Check that list is empty to avoid a race where txq_id is
+                * already updated, but the queue allocation work wasn't
+                * finished
+                */
+               if (unlikely(txq->sta && !list_empty(&mvmtxq->list)))
+                       return;
+
+               iwl_mvm_mac_itxq_xmit(hw, txq);
+               return;
+       }
+
+       /* The list is being deleted only after the queue is fully allocated. */
+       if (!list_empty(&mvmtxq->list))
+               return;
+
+       list_add_tail(&mvmtxq->list, &mvm->add_stream_txqs);
+       schedule_work(&mvm->add_stream_wk);
 }
 
 #define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...)          \
@@ -974,7 +1075,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                        mvmvif = iwl_mvm_vif_from_mac80211(vif);
                        cancel_delayed_work(&mvmvif->uapsd_nonagg_detected_wk);
                }
-               if (!iwl_enable_rx_ampdu(mvm->cfg)) {
+               if (!iwl_enable_rx_ampdu()) {
                        ret = -EINVAL;
                        break;
                }
@@ -986,7 +1087,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                                         timeout);
                break;
        case IEEE80211_AMPDU_TX_START:
-               if (!iwl_enable_tx_ampdu(mvm->cfg)) {
+               if (!iwl_enable_tx_ampdu()) {
                        ret = -EINVAL;
                        break;
                }
@@ -1066,6 +1167,8 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 
        iwl_mvm_stop_device(mvm);
 
+       mvm->cur_aid = 0;
+
        mvm->scan_status = 0;
        mvm->ps_disabled = false;
        mvm->calibrating = false;
@@ -1085,7 +1188,6 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 
        iwl_mvm_reset_phy_ctxts(mvm);
        memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
-       memset(mvm->sta_deferred_frames, 0, sizeof(mvm->sta_deferred_frames));
        memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
        memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
 
@@ -1391,6 +1493,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        if (ret)
                goto out_unlock;
 
+       rcu_assign_pointer(mvm->vif_id_to_mac[mvmvif->id], vif);
+
        /* Counting number of interfaces is needed for legacy PM */
        if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count++;
@@ -1582,6 +1686,8 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
        iwl_mvm_power_update_mac(mvm);
        iwl_mvm_mac_ctxt_remove(mvm, vif);
 
+       RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL);
+
        if (vif->type == NL80211_IFTYPE_MONITOR)
                mvm->monitor_on = false;
 
@@ -2146,6 +2252,12 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
        }
 
+       /* Update MU EDCA params */
+       if (changes & BSS_CHANGED_QOS && mvmvif->associated &&
+           bss_conf->assoc && vif->bss_conf.he_support &&
+           !iwlwifi_mod_params.disable_11ax)
+               iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id);
+
        /*
         * If we're not associated yet, take the (new) BSSID before associating
         * so the firmware knows. If we're already associated, then use the old
@@ -2672,7 +2784,7 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
                return;
 
        spin_lock_bh(&mvmsta->lock);
-       for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+       for (tid = 0; tid < ARRAY_SIZE(mvmsta->tid_data); tid++) {
                struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
 
                if (tid_data->txq_id == IWL_MVM_INVALID_QUEUE)
@@ -2861,32 +2973,6 @@ iwl_mvm_tdls_check_trigger(struct iwl_mvm *mvm,
                                peer_addr, action);
 }
 
-static void iwl_mvm_purge_deferred_tx_frames(struct iwl_mvm *mvm,
-                                            struct iwl_mvm_sta *mvm_sta)
-{
-       struct iwl_mvm_tid_data *tid_data;
-       struct sk_buff *skb;
-       int i;
-
-       spin_lock_bh(&mvm_sta->lock);
-       for (i = 0; i <= IWL_MAX_TID_COUNT; i++) {
-               tid_data = &mvm_sta->tid_data[i];
-
-               while ((skb = __skb_dequeue(&tid_data->deferred_tx_frames))) {
-                       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
-                       /*
-                        * The first deferred frame should've stopped the MAC
-                        * queues, so we should never get a second deferred
-                        * frame for the RA/TID.
-                        */
-                       iwl_mvm_start_mac_queues(mvm, BIT(info->hw_queue));
-                       ieee80211_free_txskb(mvm->hw, skb);
-               }
-       }
-       spin_unlock_bh(&mvm_sta->lock);
-}
-
 static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif,
                                 struct ieee80211_sta *sta,
@@ -2920,7 +3006,6 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
         */
        if (old_state == IEEE80211_STA_NONE &&
            new_state == IEEE80211_STA_NOTEXIST) {
-               iwl_mvm_purge_deferred_tx_frames(mvm, mvm_sta);
                flush_work(&mvm->add_stream_wk);
 
                /*
@@ -2967,6 +3052,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                        iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
                                                   NL80211_TDLS_SETUP);
                }
+
+               sta->max_rc_amsdu_len = 1;
        } else if (old_state == IEEE80211_STA_NONE &&
                   new_state == IEEE80211_STA_AUTH) {
                /*
@@ -3036,6 +3123,16 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                        iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
                                                   NL80211_TDLS_DISABLE_LINK);
                }
+
+               /* Remove STA key if this is an AP using WEP */
+               if (vif->type == NL80211_IFTYPE_AP && mvmvif->ap_wep_key) {
+                       int rm_ret = iwl_mvm_remove_sta_key(mvm, vif, sta,
+                                                           mvmvif->ap_wep_key);
+
+                       if (!ret)
+                               ret = rm_ret;
+               }
+
        } else {
                ret = -EIO;
        }
@@ -3431,14 +3528,20 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
                .id_and_color =
                        cpu_to_le32(FW_CMD_ID_AND_COLOR(MAC_INDEX_AUX, 0)),
                .sta_id_and_color = cpu_to_le32(mvm->aux_sta.sta_id),
-               /* Set the channel info data */
-               .channel_info.band = (channel->band == NL80211_BAND_2GHZ) ?
-                       PHY_BAND_24 : PHY_BAND_5,
-               .channel_info.channel = channel->hw_value,
-               .channel_info.width = PHY_VHT_CHANNEL_MODE20,
-               /* Set the time and duration */
-               .apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)),
-        };
+       };
+       struct iwl_hs20_roc_req_tail *tail = iwl_mvm_chan_info_cmd_tail(mvm,
+               &aux_roc_req.channel_info);
+       u16 len = sizeof(aux_roc_req) - iwl_mvm_chan_info_padding(mvm);
+
+       /* Set the channel info data */
+       iwl_mvm_set_chan_info(mvm, &aux_roc_req.channel_info, channel->hw_value,
+                             (channel->band == NL80211_BAND_2GHZ) ?
+                              PHY_BAND_24 : PHY_BAND_5,
+                             PHY_VHT_CHANNEL_MODE20,
+                             0);
+
+       /* Set the time and duration */
+       tail->apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg));
 
        delay = AUX_ROC_MIN_DELAY;
        req_dur = MSEC_TO_TU(duration);
@@ -3463,15 +3566,15 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
                }
        }
 
-       aux_roc_req.duration = cpu_to_le32(req_dur);
-       aux_roc_req.apply_time_max_delay = cpu_to_le32(delay);
+       tail->duration = cpu_to_le32(req_dur);
+       tail->apply_time_max_delay = cpu_to_le32(delay);
 
        IWL_DEBUG_TE(mvm,
                     "ROC: Requesting to remain on channel %u for %ums (requested = %ums, max_delay = %ums, dtim_interval = %ums)\n",
                     channel->hw_value, req_dur, duration, delay,
                     dtim_interval);
        /* Set the node address */
-       memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN);
+       memcpy(tail->node_addr, vif->addr, ETH_ALEN);
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -3502,7 +3605,7 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
                                   ARRAY_SIZE(time_event_response),
                                   iwl_mvm_rx_aux_roc, te_data);
 
-       res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, sizeof(aux_roc_req),
+       res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, len,
                                   &aux_roc_req);
 
        if (res) {
@@ -4656,8 +4759,35 @@ static void iwl_mvm_sync_rx_queues(struct ieee80211_hw *hw)
        mutex_unlock(&mvm->mutex);
 }
 
+static bool iwl_mvm_can_hw_csum(struct sk_buff *skb)
+{
+       u8 protocol = ip_hdr(skb)->protocol;
+
+       if (!IS_ENABLED(CONFIG_INET))
+               return false;
+
+       return protocol == IPPROTO_TCP || protocol == IPPROTO_UDP;
+}
+
+static bool iwl_mvm_mac_can_aggregate(struct ieee80211_hw *hw,
+                                     struct sk_buff *head,
+                                     struct sk_buff *skb)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       /* For now don't aggregate IPv6 in AMSDU */
+       if (skb->protocol != htons(ETH_P_IP))
+               return false;
+
+       if (!iwl_mvm_is_csum_supported(mvm))
+               return true;
+
+       return iwl_mvm_can_hw_csum(skb) == iwl_mvm_can_hw_csum(head);
+}
+
 const struct ieee80211_ops iwl_mvm_hw_ops = {
        .tx = iwl_mvm_mac_tx,
+       .wake_tx_queue = iwl_mvm_mac_wake_tx_queue,
        .ampdu_action = iwl_mvm_mac_ampdu_action,
        .start = iwl_mvm_mac_start,
        .reconfig_complete = iwl_mvm_mac_reconfig_complete,
@@ -4731,6 +4861,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
 #endif
        .get_survey = iwl_mvm_mac_get_survey,
        .sta_statistics = iwl_mvm_mac_sta_statistics,
+       .can_aggregate_in_amsdu = iwl_mvm_mac_can_aggregate,
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        .sta_add_debugfs = iwl_mvm_sta_add_debugfs,
 #endif
index 1aa690e..12e9ecc 100644 (file)
@@ -83,7 +83,6 @@
 #include "sta.h"
 #include "fw-api.h"
 #include "constants.h"
-#include "tof.h"
 #include "fw/runtime.h"
 #include "fw/dbg.h"
 #include "fw/acpi.h"
@@ -95,6 +94,8 @@
 /* RSSI offset for WkP */
 #define IWL_RSSI_OFFSET 50
 #define IWL_MVM_MISSED_BEACONS_THRESHOLD 8
+#define IWL_MVM_MISSED_BEACONS_THRESHOLD_LONG 16
+
 /* A TimeUnit is 1024 microsecond */
 #define MSEC_TO_TU(_msec)      (_msec*1000/1024)
 
@@ -299,17 +300,38 @@ enum iwl_bt_force_ant_mode {
 };
 
 /**
+ * struct iwl_mvm_low_latency_force - low latency force mode set by debugfs
+ * @LOW_LATENCY_FORCE_UNSET: unset force mode
+ * @LOW_LATENCY_FORCE_ON: for low latency on
+ * @LOW_LATENCY_FORCE_OFF: for low latency off
+ * @NUM_LOW_LATENCY_FORCE: max num of modes
+ */
+enum iwl_mvm_low_latency_force {
+       LOW_LATENCY_FORCE_UNSET,
+       LOW_LATENCY_FORCE_ON,
+       LOW_LATENCY_FORCE_OFF,
+       NUM_LOW_LATENCY_FORCE
+};
+
+/**
 * struct iwl_mvm_low_latency_cause - low latency set causes
 * @LOW_LATENCY_TRAFFIC: indicates low latency traffic was detected
 * @LOW_LATENCY_DEBUGFS: low latency mode set from debugfs
 * @LOW_LATENCY_VCMD: low latency mode set from vendor command
 * @LOW_LATENCY_VIF_TYPE: low latency mode set because of vif type (ap)
+* @LOW_LATENCY_DEBUGFS_FORCE_ENABLE: indicate that force mode is enabled
+*      the actual set/unset is done with LOW_LATENCY_DEBUGFS_FORCE
+* @LOW_LATENCY_DEBUGFS_FORCE: low latency force mode from debugfs
+*      set this with LOW_LATENCY_DEBUGFS_FORCE_ENABLE flag
+*      in low_latency.
 */
 enum iwl_mvm_low_latency_cause {
        LOW_LATENCY_TRAFFIC = BIT(0),
        LOW_LATENCY_DEBUGFS = BIT(1),
        LOW_LATENCY_VCMD = BIT(2),
        LOW_LATENCY_VIF_TYPE = BIT(3),
+       LOW_LATENCY_DEBUGFS_FORCE_ENABLE = BIT(4),
+       LOW_LATENCY_DEBUGFS_FORCE = BIT(5),
 };
 
 /**
@@ -360,8 +382,10 @@ struct iwl_probe_resp_data {
  * @pm_enabled - Indicate if MAC power management is allowed
  * @monitor_active: indicates that monitor context is configured, and that the
  *     interface should get quota etc.
- * @low_latency: indicates low latency is set, see
- *     enum &iwl_mvm_low_latency_cause for causes.
+ * @low_latency: bit flags for low latency
+ *     see enum &iwl_mvm_low_latency_cause for causes.
+ * @low_latency_actual: boolean, indicates low latency is set,
+ *     as a result from low_latency bit flags and takes force into account.
  * @ps_disabled: indicates that this interface requires PS to be disabled
  * @queue_params: QoS params for this MAC
  * @bcast_sta: station used for broadcast packets. Used by the following
@@ -393,7 +417,8 @@ struct iwl_mvm_vif {
        bool ap_ibss_active;
        bool pm_enabled;
        bool monitor_active;
-       u8 low_latency;
+       u8 low_latency: 6;
+       u8 low_latency_actual: 1;
        bool ps_disabled;
        struct iwl_mvm_vif_bf_data bf_data;
 
@@ -778,6 +803,39 @@ struct iwl_mvm_geo_profile {
        u8 values[ACPI_GEO_TABLE_SIZE];
 };
 
+struct iwl_mvm_txq {
+       struct list_head list;
+       u16 txq_id;
+       atomic_t tx_request;
+       bool stopped;
+};
+
+static inline struct iwl_mvm_txq *
+iwl_mvm_txq_from_mac80211(struct ieee80211_txq *txq)
+{
+       return (void *)txq->drv_priv;
+}
+
+static inline struct iwl_mvm_txq *
+iwl_mvm_txq_from_tid(struct ieee80211_sta *sta, u8 tid)
+{
+       if (tid == IWL_MAX_TID_COUNT)
+               tid = IEEE80211_NUM_TIDS;
+
+       return (void *)sta->txq[tid]->drv_priv;
+}
+
+/**
+ * struct iwl_mvm_tvqm_txq_info - maps TVQM hw queue to tid
+ *
+ * @sta_id: sta id
+ * @txq_tid: txq tid
+ */
+struct iwl_mvm_tvqm_txq_info {
+       u8 sta_id;
+       u8 txq_tid;
+};
+
 struct iwl_mvm_dqa_txq_info {
        u8 ra_sta_id; /* The RA this queue is mapped to, if exists */
        bool reserved; /* Is this the TXQ reserved for a STA */
@@ -843,13 +901,13 @@ struct iwl_mvm {
                u64 on_time_scan;
        } radio_stats, accu_radio_stats;
 
-       u16 hw_queue_to_mac80211[IWL_MAX_TVQM_QUEUES];
-
-       struct iwl_mvm_dqa_txq_info queue_info[IWL_MAX_HW_QUEUES];
+       struct list_head add_stream_txqs;
+       union {
+               struct iwl_mvm_dqa_txq_info queue_info[IWL_MAX_HW_QUEUES];
+               struct iwl_mvm_tvqm_txq_info tvqm_info[IWL_MAX_TVQM_QUEUES];
+       };
        struct work_struct add_stream_wk; /* To add streams to queues */
 
-       atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES];
-
        const char *nvm_file_name;
        struct iwl_nvm_data *nvm_data;
        /* NVM sections */
@@ -863,7 +921,6 @@ struct iwl_mvm {
        /* data related to data path */
        struct iwl_rx_phy_info last_phy_info;
        struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT];
-       unsigned long sta_deferred_frames[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)];
        u8 rx_ba_sessions;
 
        /* configured by mac80211 */
@@ -932,6 +989,7 @@ struct iwl_mvm {
        struct debugfs_blob_wrapper nvm_calib_blob;
        struct debugfs_blob_wrapper nvm_prod_blob;
        struct debugfs_blob_wrapper nvm_phy_sku_blob;
+       struct debugfs_blob_wrapper nvm_reg_blob;
 
        struct iwl_mvm_frame_stats drv_rx_stats;
        spinlock_t drv_stats_lock;
@@ -955,6 +1013,7 @@ struct iwl_mvm {
        u8 refs[IWL_MVM_REF_COUNT];
 
        u8 vif_count;
+       struct ieee80211_vif __rcu *vif_id_to_mac[NUM_MAC_INDEX_DRIVER];
 
        /* -1 for always, 0 for never, >0 for that many times */
        s8 fw_restart;
@@ -1090,7 +1149,6 @@ struct iwl_mvm {
 
        u32 ciphers[IWL_MVM_NUM_CIPHERS];
        struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
-       struct iwl_mvm_tof_data tof_data;
 
        struct ieee80211_vif *nan_vif;
 #define IWL_MAX_BAID   32
@@ -1106,6 +1164,10 @@ struct iwl_mvm {
 
        /* does a monitor vif exist (only one can exist hence bool) */
        bool monitor_on;
+
+       /* sniffer data to include in radiotap */
+       __le16 cur_aid;
+
 #ifdef CONFIG_ACPI
        struct iwl_mvm_sar_profile sar_profiles[ACPI_SAR_PROFILE_NUM];
        struct iwl_mvm_geo_profile geo_profiles[ACPI_NUM_GEO_PROFILES];
@@ -1150,7 +1212,6 @@ enum iwl_mvm_init_status {
        IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE = BIT(0),
        IWL_MVM_INIT_STATUS_LEDS_INIT_COMPLETE = BIT(1),
        IWL_MVM_INIT_STATUS_REG_HW_INIT_COMPLETE = BIT(2),
-       IWL_MVM_INIT_STATUS_TOF_INIT_COMPLETE = BIT(3),
 };
 
 static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
@@ -1207,6 +1268,19 @@ iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
        return iwl_mvm_sta_from_mac80211(sta);
 }
 
+static inline struct ieee80211_vif *
+iwl_mvm_rcu_dereference_vif_id(struct iwl_mvm *mvm, u8 vif_id, bool rcu)
+{
+       if (WARN_ON(vif_id >= ARRAY_SIZE(mvm->vif_id_to_mac)))
+               return NULL;
+
+       if (rcu)
+               return rcu_dereference(mvm->vif_id_to_mac[vif_id]);
+
+       return rcu_dereference_protected(mvm->vif_id_to_mac[vif_id],
+                                        lockdep_is_held(&mvm->mutex));
+}
+
 static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm)
 {
        return !iwlwifi_mod_params.d0i3_disable &&
@@ -1470,6 +1544,11 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
 void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
                            struct ieee80211_tx_info *info,
                            struct ieee80211_sta *sta, __le16 fc);
+void iwl_mvm_mac_itxq_xmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
+unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm,
+                                   struct ieee80211_sta *sta,
+                                   unsigned int tid);
+
 #ifdef CONFIG_IWLWIFI_DEBUG
 const char *iwl_mvm_get_tx_fail_reason(u32 status);
 #else
@@ -1599,7 +1678,6 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                             bool force_assoc_off, const u8 *bssid_override);
 int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif);
 int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif);
 void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
@@ -1615,8 +1693,6 @@ void iwl_mvm_window_status_notif(struct iwl_mvm *mvm,
                                 struct iwl_rx_cmd_buffer *rxb);
 void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif);
-unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
-                                        struct ieee80211_vif *exclude_vif);
 void iwl_mvm_probe_resp_data_notif(struct iwl_mvm *mvm,
                                   struct iwl_rx_cmd_buffer *rxb);
 void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm,
@@ -1870,17 +1946,43 @@ static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif)
         * binding, so this has no real impact. For now, just return
         * the current desired low-latency state.
         */
-       return mvmvif->low_latency;
+       return mvmvif->low_latency_actual;
 }
 
 static inline
 void iwl_mvm_vif_set_low_latency(struct iwl_mvm_vif *mvmvif, bool set,
                                 enum iwl_mvm_low_latency_cause cause)
 {
+       u8 new_state;
+
        if (set)
                mvmvif->low_latency |= cause;
        else
                mvmvif->low_latency &= ~cause;
+
+       /*
+        * if LOW_LATENCY_DEBUGFS_FORCE_ENABLE is enabled no changes are
+        * allowed to actual mode.
+        */
+       if (mvmvif->low_latency & LOW_LATENCY_DEBUGFS_FORCE_ENABLE &&
+           cause != LOW_LATENCY_DEBUGFS_FORCE_ENABLE)
+               return;
+
+       if (cause == LOW_LATENCY_DEBUGFS_FORCE_ENABLE && set)
+               /*
+                * We enter force state
+                */
+               new_state = !!(mvmvif->low_latency &
+                              LOW_LATENCY_DEBUGFS_FORCE);
+       else
+               /*
+                * Check if any other one set low latency
+                */
+               new_state = !!(mvmvif->low_latency &
+                                 ~(LOW_LATENCY_DEBUGFS_FORCE_ENABLE |
+                                   LOW_LATENCY_DEBUGFS_FORCE));
+
+       mvmvif->low_latency_actual = new_state;
 }
 
 /* Return a bitmask with all the hw supported queues, except for the
@@ -1895,6 +1997,16 @@ static inline u32 iwl_mvm_flushable_queues(struct iwl_mvm *mvm)
 static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm)
 {
        lockdep_assert_held(&mvm->mutex);
+       /* If IWL_MVM_STATUS_HW_RESTART_REQUESTED bit is set then we received
+        * an assert. Since we failed to bring the interface up, mac80211
+        * will not attempt to reconfig the device,
+        * which handles the dump collection in assert flow,
+        * so trigger dump collection here.
+        */
+       if (test_and_clear_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
+                              &mvm->status))
+               iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
+                                       false, 0);
        /* calling this function without using dump_start/end since at this
         * point we already hold the op mode mutex
         */
@@ -1906,10 +2018,6 @@ static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm)
        iwl_trans_stop_device(mvm->trans);
 }
 
-/* Stop/start all mac queues in a given bitmap */
-void iwl_mvm_start_mac_queues(struct iwl_mvm *mvm, unsigned long mq);
-void iwl_mvm_stop_mac_queues(struct iwl_mvm *mvm, unsigned long mq);
-
 /* Re-configure the SCD for a queue that has already been configured */
 int iwl_mvm_reconfig_scd(struct iwl_mvm *mvm, int queue, int fifo, int sta_id,
                         int tid, int frame_limit, u16 ssn);
@@ -2015,4 +2123,59 @@ void iwl_mvm_sta_add_debugfs(struct ieee80211_hw *hw,
                             struct dentry *dir);
 #endif
 
+/* Channel info utils */
+static inline bool iwl_mvm_has_ultra_hb_channel(struct iwl_mvm *mvm)
+{
+       return fw_has_capa(&mvm->fw->ucode_capa,
+                          IWL_UCODE_TLV_CAPA_ULTRA_HB_CHANNELS);
+}
+
+static inline void *iwl_mvm_chan_info_cmd_tail(struct iwl_mvm *mvm,
+                                              struct iwl_fw_channel_info *ci)
+{
+       return (u8 *)ci + (iwl_mvm_has_ultra_hb_channel(mvm) ?
+                          sizeof(struct iwl_fw_channel_info) :
+                          sizeof(struct iwl_fw_channel_info_v1));
+}
+
+static inline size_t iwl_mvm_chan_info_padding(struct iwl_mvm *mvm)
+{
+       return iwl_mvm_has_ultra_hb_channel(mvm) ? 0 :
+               sizeof(struct iwl_fw_channel_info) -
+               sizeof(struct iwl_fw_channel_info_v1);
+}
+
+static inline void iwl_mvm_set_chan_info(struct iwl_mvm *mvm,
+                                        struct iwl_fw_channel_info *ci,
+                                        u32 chan, u8 band, u8 width,
+                                        u8 ctrl_pos)
+{
+       if (iwl_mvm_has_ultra_hb_channel(mvm)) {
+               ci->channel = cpu_to_le32(chan);
+               ci->band = band;
+               ci->width = width;
+               ci->ctrl_pos = ctrl_pos;
+       } else {
+               struct iwl_fw_channel_info_v1 *ci_v1 =
+                                       (struct iwl_fw_channel_info_v1 *)ci;
+
+               ci_v1->channel = chan;
+               ci_v1->band = band;
+               ci_v1->width = width;
+               ci_v1->ctrl_pos = ctrl_pos;
+       }
+}
+
+static inline void
+iwl_mvm_set_chan_info_chandef(struct iwl_mvm *mvm,
+                             struct iwl_fw_channel_info *ci,
+                             struct cfg80211_chan_def *chandef)
+{
+       iwl_mvm_set_chan_info(mvm, ci, chandef->chan->hw_value,
+                             (chandef->chan->band == NL80211_BAND_2GHZ ?
+                              PHY_BAND_24 : PHY_BAND_5),
+                              iwl_mvm_get_channel_width(chandef),
+                              iwl_mvm_get_ctrl_pos(chandef));
+}
+
 #endif /* __IWL_MVM_H__ */
index 6fc5cc1..7bdbd01 100644 (file)
@@ -179,7 +179,7 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
                        IWL_DEBUG_EEPROM(mvm->trans->dev,
                                         "NVM access command failed with status %d (device: %s)\n",
                                         ret, mvm->cfg->name);
-                       ret = -EIO;
+                       ret = -ENODATA;
                }
                goto exit;
        }
@@ -380,8 +380,12 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
                /* we override the constness for initial read */
                ret = iwl_nvm_read_section(mvm, section, nvm_buffer,
                                           size_read);
-               if (ret < 0)
+               if (ret == -ENODATA) {
+                       ret = 0;
                        continue;
+               }
+               if (ret < 0)
+                       break;
                size_read += ret;
                temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
                if (!temp) {
@@ -412,6 +416,11 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
                        mvm->nvm_phy_sku_blob.data = temp;
                        mvm->nvm_phy_sku_blob.size  = ret;
                        break;
+               case NVM_SECTION_TYPE_REGULATORY_SDP:
+               case NVM_SECTION_TYPE_REGULATORY:
+                       mvm->nvm_reg_blob.data = temp;
+                       mvm->nvm_reg_blob.size  = ret;
+                       break;
                default:
                        if (section == mvm->cfg->nvm_hw_section_num) {
                                mvm->nvm_hw_blob.data = temp;
@@ -454,7 +463,7 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
        IWL_DEBUG_EEPROM(mvm->trans->dev, "nvm version = %x\n",
                         mvm->nvm_data->nvm_version);
 
-       return 0;
+       return ret < 0 ? ret : 0;
 }
 
 struct iwl_mcc_update_resp *
index 30c5127..5e4f8b7 100644 (file)
@@ -301,8 +301,6 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
                   RX_HANDLER_ASYNC_LOCKED),
        RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif,
                   RX_HANDLER_SYNC),
-       RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler,
-                  RX_HANDLER_ASYNC_LOCKED),
        RX_HANDLER_GRP(DEBUG_GROUP, MFU_ASSERT_DUMP_NTF,
                       iwl_mvm_mfu_assert_dump_notif, RX_HANDLER_SYNC),
        RX_HANDLER_GRP(PROT_OFFLOAD_GROUP, STORED_BEACON_NTF,
@@ -329,8 +327,6 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = {
        HCMD_NAME(SCAN_REQ_UMAC),
        HCMD_NAME(SCAN_ABORT_UMAC),
        HCMD_NAME(SCAN_COMPLETE_UMAC),
-       HCMD_NAME(TOF_CMD),
-       HCMD_NAME(TOF_NOTIFICATION),
        HCMD_NAME(BA_WINDOW_STATUS_NOTIFICATION_ID),
        HCMD_NAME(ADD_STA_KEY),
        HCMD_NAME(ADD_STA),
@@ -449,6 +445,7 @@ static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = {
        HCMD_NAME(TRIGGER_RX_QUEUES_NOTIF_CMD),
        HCMD_NAME(STA_HE_CTXT_CMD),
        HCMD_NAME(RFH_QUEUE_CONFIG_CMD),
+       HCMD_NAME(CHEST_COLLECTOR_FILTER_CONFIG_CMD),
        HCMD_NAME(STA_PM_NOTIF),
        HCMD_NAME(MU_GROUP_MGMT_NOTIF),
        HCMD_NAME(RX_QUEUES_NOTIFICATION),
@@ -464,6 +461,22 @@ static const struct iwl_hcmd_names iwl_mvm_debug_names[] = {
 /* Please keep this array *SORTED* by hex value.
  * Access is done through binary search
  */
+static const struct iwl_hcmd_names iwl_mvm_location_names[] = {
+       HCMD_NAME(TOF_RANGE_REQ_CMD),
+       HCMD_NAME(TOF_CONFIG_CMD),
+       HCMD_NAME(TOF_RANGE_ABORT_CMD),
+       HCMD_NAME(TOF_RANGE_REQ_EXT_CMD),
+       HCMD_NAME(TOF_RESPONDER_CONFIG_CMD),
+       HCMD_NAME(TOF_RESPONDER_DYN_CONFIG_CMD),
+       HCMD_NAME(TOF_LC_NOTIF),
+       HCMD_NAME(TOF_RESPONDER_STATS),
+       HCMD_NAME(TOF_MCSI_DEBUG_NOTIF),
+       HCMD_NAME(TOF_RANGE_RESPONSE_NOTIF),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
 static const struct iwl_hcmd_names iwl_mvm_prot_offload_names[] = {
        HCMD_NAME(STORED_BEACON_NTF),
 };
@@ -483,6 +496,7 @@ static const struct iwl_hcmd_arr iwl_mvm_groups[] = {
        [MAC_CONF_GROUP] = HCMD_ARR(iwl_mvm_mac_conf_names),
        [PHY_OPS_GROUP] = HCMD_ARR(iwl_mvm_phy_names),
        [DATA_PATH_GROUP] = HCMD_ARR(iwl_mvm_data_path_names),
+       [LOCATION_GROUP] = HCMD_ARR(iwl_mvm_location_names),
        [PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mvm_prot_offload_names),
        [REGULATORY_AND_NVM_GROUP] =
                HCMD_ARR(iwl_mvm_regulatory_and_nvm_names),
@@ -685,6 +699,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work);
        INIT_DELAYED_WORK(&mvm->scan_timeout_dwork, iwl_mvm_scan_timeout_wk);
        INIT_WORK(&mvm->add_stream_wk, iwl_mvm_add_new_dqa_stream_wk);
+       INIT_LIST_HEAD(&mvm->add_stream_txqs);
 
        spin_lock_init(&mvm->d0i3_tx_lock);
        spin_lock_init(&mvm->refs_lock);
@@ -736,6 +751,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                trans_cfg.rx_buf_size = rb_size_default;
        }
 
+       BUILD_BUG_ON(sizeof(struct iwl_ldbg_config_cmd) !=
+                    LDBG_CFG_COMMAND_SIZE);
+
        trans->wide_cmd_header = true;
        trans_cfg.bc_table_dword =
                mvm->trans->cfg->device_family < IWL_DEVICE_FAMILY_22560;
@@ -842,8 +860,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        if (iwl_mvm_is_d0i3_supported(mvm))
                iwl_trans_unref(mvm->trans);
 
-       iwl_mvm_tof_init(mvm);
-
        iwl_mvm_toggle_tx_ant(mvm, &mvm->mgmt_last_antenna_idx);
 
        return op_mode;
@@ -909,8 +925,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
 
        cancel_delayed_work_sync(&mvm->tcm.work);
 
-       iwl_mvm_tof_clean(mvm);
-
        iwl_fw_runtime_free(&mvm->fwrt);
        mutex_destroy(&mvm->mutex);
        mutex_destroy(&mvm->d0i3_suspend_mutex);
@@ -1079,24 +1093,6 @@ static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode,
                iwl_mvm_rx_common(mvm, rxb, pkt);
 }
 
-void iwl_mvm_stop_mac_queues(struct iwl_mvm *mvm, unsigned long mq)
-{
-       int q;
-
-       if (WARN_ON_ONCE(!mq))
-               return;
-
-       for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) {
-               if (atomic_inc_return(&mvm->mac80211_queue_stop_count[q]) > 1) {
-                       IWL_DEBUG_TX_QUEUES(mvm,
-                                           "mac80211 %d already stopped\n", q);
-                       continue;
-               }
-
-               ieee80211_stop_queue(mvm->hw, q);
-       }
-}
-
 static void iwl_mvm_async_cb(struct iwl_op_mode *op_mode,
                             const struct iwl_device_cmd *cmd)
 {
@@ -1109,38 +1105,66 @@ static void iwl_mvm_async_cb(struct iwl_op_mode *op_mode,
        iwl_trans_block_txq_ptrs(mvm->trans, false);
 }
 
-static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int hw_queue)
+static void iwl_mvm_queue_state_change(struct iwl_op_mode *op_mode,
+                                      int hw_queue, bool start)
 {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-       unsigned long mq = mvm->hw_queue_to_mac80211[hw_queue];
+       struct ieee80211_sta *sta;
+       struct ieee80211_txq *txq;
+       struct iwl_mvm_txq *mvmtxq;
+       int i;
+       unsigned long tid_bitmap;
+       struct iwl_mvm_sta *mvmsta;
+       u8 sta_id;
 
-       iwl_mvm_stop_mac_queues(mvm, mq);
-}
+       sta_id = iwl_mvm_has_new_tx_api(mvm) ?
+               mvm->tvqm_info[hw_queue].sta_id :
+               mvm->queue_info[hw_queue].ra_sta_id;
 
-void iwl_mvm_start_mac_queues(struct iwl_mvm *mvm, unsigned long mq)
-{
-       int q;
-
-       if (WARN_ON_ONCE(!mq))
+       if (WARN_ON_ONCE(sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id)))
                return;
 
-       for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) {
-               if (atomic_dec_return(&mvm->mac80211_queue_stop_count[q]) > 0) {
-                       IWL_DEBUG_TX_QUEUES(mvm,
-                                           "mac80211 %d still stopped\n", q);
-                       continue;
-               }
+       rcu_read_lock();
+
+       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+       if (IS_ERR_OR_NULL(sta))
+               goto out;
+       mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
-               ieee80211_wake_queue(mvm->hw, q);
+       if (iwl_mvm_has_new_tx_api(mvm)) {
+               int tid = mvm->tvqm_info[hw_queue].txq_tid;
+
+               tid_bitmap = BIT(tid);
+       } else {
+               tid_bitmap = mvm->queue_info[hw_queue].tid_bitmap;
        }
+
+       for_each_set_bit(i, &tid_bitmap, IWL_MAX_TID_COUNT + 1) {
+               int tid = i;
+
+               if (tid == IWL_MAX_TID_COUNT)
+                       tid = IEEE80211_NUM_TIDS;
+
+               txq = sta->txq[tid];
+               mvmtxq = iwl_mvm_txq_from_mac80211(txq);
+               mvmtxq->stopped = !start;
+
+               if (start && mvmsta->sta_state != IEEE80211_STA_NOTEXIST)
+                       iwl_mvm_mac_itxq_xmit(mvm->hw, txq);
+       }
+
+out:
+       rcu_read_unlock();
 }
 
-static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int hw_queue)
+static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int hw_queue)
 {
-       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-       unsigned long mq = mvm->hw_queue_to_mac80211[hw_queue];
+       iwl_mvm_queue_state_change(op_mode, hw_queue, false);
+}
 
-       iwl_mvm_start_mac_queues(mvm, mq);
+static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int hw_queue)
+{
+       iwl_mvm_queue_state_change(op_mode, hw_queue, true);
 }
 
 static void iwl_mvm_set_rfkill_state(struct iwl_mvm *mvm)
index 7f5434b..f369173 100644 (file)
@@ -143,14 +143,11 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
                                      u8 chains_static, u8 chains_dynamic)
 {
        u8 active_cnt, idle_cnt;
+       struct iwl_phy_context_cmd_tail *tail =
+               iwl_mvm_chan_info_cmd_tail(mvm, &cmd->ci);
 
        /* Set the channel info data */
-       cmd->ci.band = (chandef->chan->band == NL80211_BAND_2GHZ ?
-             PHY_BAND_24 : PHY_BAND_5);
-
-       cmd->ci.channel = chandef->chan->hw_value;
-       cmd->ci.width = iwl_mvm_get_channel_width(chandef);
-       cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
+       iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
 
        /* Set rx the chains */
        idle_cnt = chains_static;
@@ -168,17 +165,17 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
                active_cnt = 2;
        }
 
-       cmd->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
+       tail->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
                                        PHY_RX_CHAIN_VALID_POS);
-       cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
-       cmd->rxchain_info |= cpu_to_le32(active_cnt <<
+       tail->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
+       tail->rxchain_info |= cpu_to_le32(active_cnt <<
                                         PHY_RX_CHAIN_MIMO_CNT_POS);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        if (unlikely(mvm->dbgfs_rx_phyinfo))
-               cmd->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
+               tail->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
 #endif
 
-       cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
+       tail->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
 }
 
 /*
@@ -195,6 +192,7 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
 {
        struct iwl_phy_context_cmd cmd;
        int ret;
+       u16 len = sizeof(cmd) - iwl_mvm_chan_info_padding(mvm);
 
        /* Set the command header fields */
        iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time);
@@ -203,9 +201,7 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
        iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef,
                                  chains_static, chains_dynamic);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0,
-                                  sizeof(struct iwl_phy_context_cmd),
-                                  &cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0, len, &cmd);
        if (ret)
                IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
        return ret;
index dabbc04..a28283f 100644 (file)
@@ -149,14 +149,9 @@ static u16 rs_fw_set_config_flags(struct iwl_mvm *mvm,
 
        if (he_cap && he_cap->has_he &&
            (he_cap->he_cap_elem.phy_cap_info[3] &
-            IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK)) {
+            IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK))
                flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK;
 
-               if (he_cap->he_cap_elem.phy_cap_info[3] &
-                   IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_2)
-                       flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_2_MSK;
-       }
-
        return flags;
 }
 
@@ -320,12 +315,26 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,
 
        if (flags & IWL_TLC_NOTIF_FLAG_AMSDU) {
                u16 size = le32_to_cpu(notif->amsdu_size);
+               int i;
 
                if (WARN_ON(sta->max_amsdu_len < size))
                        goto out;
 
                mvmsta->amsdu_enabled = le32_to_cpu(notif->amsdu_enabled);
                mvmsta->max_amsdu_len = size;
+               sta->max_rc_amsdu_len = mvmsta->max_amsdu_len;
+
+               for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+                       if (mvmsta->amsdu_enabled & BIT(i))
+                               sta->max_tid_amsdu_len[i] =
+                                       iwl_mvm_max_amsdu_size(mvm, sta, i);
+                       else
+                               /*
+                                * Not so elegant, but this will effectively
+                                * prevent AMSDU on this TID
+                                */
+                               sta->max_tid_amsdu_len[i] = 1;
+               }
 
                IWL_DEBUG_RATE(mvm,
                               "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n",
index 0899722..09866f5 100644 (file)
@@ -1744,6 +1744,7 @@ static void rs_set_amsdu_len(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                             enum rs_action scale_action)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       int i;
 
        /*
         * In case TLC offload is not active amsdu_enabled is either 0xFFFF
@@ -1757,6 +1758,19 @@ static void rs_set_amsdu_len(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                mvmsta->amsdu_enabled = 0xFFFF;
 
        mvmsta->max_amsdu_len = sta->max_amsdu_len;
+       sta->max_rc_amsdu_len = mvmsta->max_amsdu_len;
+
+       for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+               if (mvmsta->amsdu_enabled)
+                       sta->max_tid_amsdu_len[i] =
+                               iwl_mvm_max_amsdu_size(mvm, sta, i);
+               else
+                       /*
+                        * Not so elegant, but this will effectively
+                        * prevent AMSDU on this TID
+                        */
+                       sta->max_tid_amsdu_len[i] = 1;
+       }
 }
 
 /*
@@ -3332,12 +3346,12 @@ static void rs_fill_rates_for_column(struct iwl_mvm *mvm,
 /* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI
  * column the rate table should look like this:
  *
- * rate[0] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
- * rate[1] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
- * rate[2] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
- * rate[3] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
- * rate[4] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
- * rate[5] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
+ * rate[0] 0x400F019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
+ * rate[1] 0x400F019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
+ * rate[2] 0x400F018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
+ * rate[3] 0x400F018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
+ * rate[4] 0x400F017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
+ * rate[5] 0x400F017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
  * rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI
  * rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI
  * rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI
index 6653a23..235ab26 100644 (file)
@@ -599,8 +599,8 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
         * data copied into the "data" struct, but rather the data from
         * the notification directly.
         */
-       if (iwl_mvm_is_cdb_supported(mvm)) {
-               struct mvm_statistics_general_cdb *general =
+       if (iwl_mvm_has_new_rx_stats_api(mvm)) {
+               struct mvm_statistics_general *general =
                        data->general;
 
                mvmvif->beacon_stats.num_beacons =
@@ -723,7 +723,7 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
                else
                        expected_size = sizeof(struct iwl_notif_statistics_v10);
        } else {
-               expected_size = sizeof(struct iwl_notif_statistics_cdb);
+               expected_size = sizeof(struct iwl_notif_statistics);
        }
 
        if (WARN_ONCE(iwl_rx_packet_payload_len(pkt) != expected_size,
@@ -753,7 +753,7 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
 
                flags = stats->flag;
        } else {
-               struct iwl_notif_statistics_cdb *stats = (void *)&pkt->data;
+               struct iwl_notif_statistics *stats = (void *)&pkt->data;
 
                data.mac_id = stats->rx.general.mac_id;
                data.beacon_filter_average_energy =
@@ -792,7 +792,7 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
                bytes = (void *)&v11->load_stats.byte_count;
                air_time = (void *)&v11->load_stats.air_time;
        } else {
-               struct iwl_notif_statistics_cdb *stats = (void *)&pkt->data;
+               struct iwl_notif_statistics *stats = (void *)&pkt->data;
 
                energy = (void *)&stats->load_stats.avg_energy;
                bytes = (void *)&stats->load_stats.byte_count;
index 7bd8676..2c56f73 100644 (file)
@@ -192,27 +192,48 @@ static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr,
        }
 }
 
+static void iwl_mvm_add_rtap_sniffer_config(struct iwl_mvm *mvm,
+                                           struct sk_buff *skb)
+{
+       struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_vendor_radiotap *radiotap;
+       int size = sizeof(*radiotap) + sizeof(__le16);
+
+       if (!mvm->cur_aid)
+               return;
+
+       radiotap = skb_put(skb, size);
+       radiotap->align = 1;
+       /* Intel OUI */
+       radiotap->oui[0] = 0xf6;
+       radiotap->oui[1] = 0x54;
+       radiotap->oui[2] = 0x25;
+       /* radiotap sniffer config sub-namespace */
+       radiotap->subns = 1;
+       radiotap->present = 0x1;
+       radiotap->len = size - sizeof(*radiotap);
+       radiotap->pad = 0;
+
+       /* fill the data now */
+       memcpy(radiotap->data, &mvm->cur_aid, sizeof(mvm->cur_aid));
+
+       rx_status->flag |= RX_FLAG_RADIOTAP_VENDOR_DATA;
+}
+
 /* iwl_mvm_pass_packet_to_mac80211 - passes the packet for mac80211 */
 static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
                                            struct napi_struct *napi,
                                            struct sk_buff *skb, int queue,
-                                           struct ieee80211_sta *sta)
+                                           struct ieee80211_sta *sta,
+                                           bool csi)
 {
        struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
 
        if (!(rx_status->flag & RX_FLAG_NO_PSDU) &&
-           iwl_mvm_check_pn(mvm, skb, queue, sta)) {
+           iwl_mvm_check_pn(mvm, skb, queue, sta))
                kfree_skb(skb);
-       } else {
-               unsigned int radiotap_len = 0;
-
-               if (rx_status->flag & RX_FLAG_RADIOTAP_HE)
-                       radiotap_len += sizeof(struct ieee80211_radiotap_he);
-               if (rx_status->flag & RX_FLAG_RADIOTAP_HE_MU)
-                       radiotap_len += sizeof(struct ieee80211_radiotap_he_mu);
-               __skb_push(skb, radiotap_len);
+       else
                ieee80211_rx_napi(mvm->hw, sta, skb, napi);
-       }
 }
 
 static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
@@ -473,7 +494,7 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm,
                while ((skb = __skb_dequeue(skb_list))) {
                        iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb,
                                                        reorder_buf->queue,
-                                                       sta);
+                                                       sta, false);
                        reorder_buf->num_stored--;
                }
        }
@@ -666,6 +687,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
         * This also covers the case of receiving a Block Ack Request
         * outside a BA session; we'll pass it to mac80211 and that
         * then sends a delBA action frame.
+        * This also covers pure monitor mode, in which case we won't
+        * have any BA sessions.
         */
        if (baid == IWL_RX_REORDER_DATA_INVALID_BAID)
                return false;
@@ -1158,14 +1181,12 @@ static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb,
        /* temporarily hide the radiotap data */
        __skb_pull(skb, radiotap_len);
 
-       if (phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_SU) {
-               /* report the AMPDU-EOF bit on single frames */
-               if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
-                       rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
-                       rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
-                       if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF))
-                               rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
-               }
+       /* report the AMPDU-EOF bit on single frames */
+       if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
+               rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
+               rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
+               if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF))
+                       rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
        }
 
        if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
@@ -1178,9 +1199,7 @@ static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb,
                bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE;
 
                /* toggle is switched whenever new aggregation starts */
-               if (toggle_bit != mvm->ampdu_toggle &&
-                   (he_type == RATE_MCS_HE_TYPE_MU ||
-                    he_type == RATE_MCS_HE_TYPE_SU)) {
+               if (toggle_bit != mvm->ampdu_toggle) {
                        rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
                        if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF))
                                rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
@@ -1314,6 +1333,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                .d4 = desc->phy_data4,
                .info_type = IWL_RX_PHY_INFO_TYPE_NONE,
        };
+       bool csi = false;
 
        if (unlikely(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)))
                return;
@@ -1412,7 +1432,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
        }
        /* set the preamble flag if appropriate */
-       if (phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE)
+       if (rate_n_flags & RATE_MCS_CCK_MSK &&
+           phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE)
                rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE;
 
        if (likely(!(phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) {
@@ -1441,14 +1462,23 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE;
 
                rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
-               rx_status->ampdu_reference = mvm->ampdu_ref;
-               /* toggle is switched whenever new aggregation starts */
+               /*
+                * Toggle is switched whenever new aggregation starts. Make
+                * sure ampdu_reference is never 0 so we can later use it to
+                * see if the frame was really part of an A-MPDU or not.
+                */
                if (toggle_bit != mvm->ampdu_toggle) {
                        mvm->ampdu_ref++;
+                       if (mvm->ampdu_ref == 0)
+                               mvm->ampdu_ref++;
                        mvm->ampdu_toggle = toggle_bit;
                }
+               rx_status->ampdu_reference = mvm->ampdu_ref;
        }
 
+       if (unlikely(mvm->monitor_on))
+               iwl_mvm_add_rtap_sniffer_config(mvm, skb);
+
        rcu_read_lock();
 
        if (desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_SRC_STA_FOUND)) {
@@ -1602,7 +1632,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
 
        iwl_mvm_create_skb(skb, hdr, len, crypt_len, rxb);
        if (!iwl_mvm_reorder(mvm, napi, queue, sta, skb, desc))
-               iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta);
+               iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue,
+                                               sta, csi);
 out:
        rcu_read_unlock();
 }
@@ -1705,15 +1736,24 @@ void iwl_mvm_rx_monitor_ndp(struct iwl_mvm *mvm, struct napi_struct *napi,
        } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
                u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >>
                                RATE_MCS_STBC_POS;
-               rx_status->nss =
-                       ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
-                                               RATE_VHT_MCS_NSS_POS) + 1;
                rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
                rx_status->encoding = RX_ENC_VHT;
                rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT;
                if (rate_n_flags & RATE_MCS_BF_MSK)
                        rx_status->enc_flags |= RX_ENC_FLAG_BF;
-       } else if (!(rate_n_flags & RATE_MCS_HE_MSK)) {
+               /*
+                * take the nss from the rx_vec since the rate_n_flags has
+                * only 2 bits for the nss which gives a max of 4 ss but
+                * there may be up to 8 spatial streams
+                */
+               rx_status->nss =
+                       le32_get_bits(desc->rx_vec[0],
+                                     RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK) + 1;
+       } else if (rate_n_flags & RATE_MCS_HE_MSK) {
+               rx_status->nss =
+                       le32_get_bits(desc->rx_vec[0],
+                                     RX_NO_DATA_RX_VEC0_HE_NSTS_MSK) + 1;
+       } else {
                int rate = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
                                                               rx_status->band);
 
@@ -1726,7 +1766,7 @@ void iwl_mvm_rx_monitor_ndp(struct iwl_mvm *mvm, struct napi_struct *napi,
                rx_status->rate_idx = rate;
        }
 
-       iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta);
+       iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta, false);
 out:
        rcu_read_unlock();
 }
index d1d76bb..9da0dae 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright (C) 2018 Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -28,6 +29,7 @@
  *
  * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright (C) 2018 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -64,7 +66,7 @@ struct iwl_mvm_active_iface_iterator_data {
        struct ieee80211_vif *ignore_vif;
        u8 sta_vif_ap_sta_id;
        enum iwl_sf_state sta_vif_state;
-       int num_active_macs;
+       u32 num_active_macs;
 };
 
 /*
index e280098..c5a0147 100644 (file)
@@ -356,24 +356,16 @@ static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue,
        return ret;
 }
 
-static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue,
-                              int mac80211_queue, u8 tid, u8 flags)
+static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                              int queue, u8 tid, u8 flags)
 {
        struct iwl_scd_txq_cfg_cmd cmd = {
                .scd_queue = queue,
                .action = SCD_CFG_DISABLE_QUEUE,
        };
-       bool remove_mac_queue = mac80211_queue != IEEE80211_INVAL_HW_QUEUE;
        int ret;
 
-       if (WARN_ON(remove_mac_queue && mac80211_queue >= IEEE80211_MAX_QUEUES))
-               return -EINVAL;
-
        if (iwl_mvm_has_new_tx_api(mvm)) {
-               if (remove_mac_queue)
-                       mvm->hw_queue_to_mac80211[queue] &=
-                               ~BIT(mac80211_queue);
-
                iwl_trans_txq_free(mvm->trans, queue);
 
                return 0;
@@ -384,36 +376,15 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue,
 
        mvm->queue_info[queue].tid_bitmap &= ~BIT(tid);
 
-       /*
-        * If there is another TID with the same AC - don't remove the MAC queue
-        * from the mapping
-        */
-       if (tid < IWL_MAX_TID_COUNT) {
-               unsigned long tid_bitmap =
-                       mvm->queue_info[queue].tid_bitmap;
-               int ac = tid_to_mac80211_ac[tid];
-               int i;
-
-               for_each_set_bit(i, &tid_bitmap, IWL_MAX_TID_COUNT) {
-                       if (tid_to_mac80211_ac[i] == ac)
-                               remove_mac_queue = false;
-               }
-       }
-
-       if (remove_mac_queue)
-               mvm->hw_queue_to_mac80211[queue] &=
-                       ~BIT(mac80211_queue);
-
        cmd.action = mvm->queue_info[queue].tid_bitmap ?
                SCD_CFG_ENABLE_QUEUE : SCD_CFG_DISABLE_QUEUE;
        if (cmd.action == SCD_CFG_DISABLE_QUEUE)
                mvm->queue_info[queue].status = IWL_MVM_QUEUE_FREE;
 
        IWL_DEBUG_TX_QUEUES(mvm,
-                           "Disabling TXQ #%d tids=0x%x (mac80211 map:0x%x)\n",
+                           "Disabling TXQ #%d tids=0x%x\n",
                            queue,
-                           mvm->queue_info[queue].tid_bitmap,
-                           mvm->hw_queue_to_mac80211[queue]);
+                           mvm->queue_info[queue].tid_bitmap);
 
        /* If the queue is still enabled - nothing left to do in this func */
        if (cmd.action == SCD_CFG_ENABLE_QUEUE)
@@ -423,15 +394,19 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue,
        cmd.tid = mvm->queue_info[queue].txq_tid;
 
        /* Make sure queue info is correct even though we overwrite it */
-       WARN(mvm->queue_info[queue].tid_bitmap ||
-            mvm->hw_queue_to_mac80211[queue],
-            "TXQ #%d info out-of-sync - mac map=0x%x, tids=0x%x\n",
-            queue, mvm->hw_queue_to_mac80211[queue],
-            mvm->queue_info[queue].tid_bitmap);
+       WARN(mvm->queue_info[queue].tid_bitmap,
+            "TXQ #%d info out-of-sync - tids=0x%x\n",
+            queue, mvm->queue_info[queue].tid_bitmap);
 
        /* If we are here - the queue is freed and we can zero out these vals */
        mvm->queue_info[queue].tid_bitmap = 0;
-       mvm->hw_queue_to_mac80211[queue] = 0;
+
+       if (sta) {
+               struct iwl_mvm_txq *mvmtxq =
+                       iwl_mvm_txq_from_tid(sta, tid);
+
+               mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;
+       }
 
        /* Regardless if this is a reserved TXQ for a STA - mark it as false */
        mvm->queue_info[queue].reserved = false;
@@ -517,9 +492,14 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue)
        spin_lock_bh(&mvmsta->lock);
        /* Unmap MAC queues and TIDs from this queue */
        for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) {
+               struct iwl_mvm_txq *mvmtxq =
+                       iwl_mvm_txq_from_tid(sta, tid);
+
                if (mvmsta->tid_data[tid].state == IWL_AGG_ON)
                        disable_agg_tids |= BIT(tid);
                mvmsta->tid_data[tid].txq_id = IWL_MVM_INVALID_QUEUE;
+
+               mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;
        }
 
        mvmsta->tfd_queue_msk &= ~BIT(queue); /* Don't use this queue anymore */
@@ -541,10 +521,11 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue)
 }
 
 static int iwl_mvm_free_inactive_queue(struct iwl_mvm *mvm, int queue,
+                                      struct ieee80211_sta *old_sta,
                                       u8 new_sta_id)
 {
        struct iwl_mvm_sta *mvmsta;
-       u8 txq_curr_ac, sta_id, tid;
+       u8 sta_id, tid;
        unsigned long disable_agg_tids = 0;
        bool same_sta;
        int ret;
@@ -554,7 +535,6 @@ static int iwl_mvm_free_inactive_queue(struct iwl_mvm *mvm, int queue,
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
 
-       txq_curr_ac = mvm->queue_info[queue].mac80211_ac;
        sta_id = mvm->queue_info[queue].ra_sta_id;
        tid = mvm->queue_info[queue].txq_tid;
 
@@ -570,9 +550,7 @@ static int iwl_mvm_free_inactive_queue(struct iwl_mvm *mvm, int queue,
                iwl_mvm_invalidate_sta_queue(mvm, queue,
                                             disable_agg_tids, false);
 
-       ret = iwl_mvm_disable_txq(mvm, queue,
-                                 mvmsta->vif->hw_queue[txq_curr_ac],
-                                 tid, 0);
+       ret = iwl_mvm_disable_txq(mvm, old_sta, queue, tid, 0);
        if (ret) {
                IWL_ERR(mvm,
                        "Failed to free inactive queue %d (ret=%d)\n",
@@ -662,16 +640,15 @@ static int iwl_mvm_get_shared_queue(struct iwl_mvm *mvm,
  * in such a case, otherwise - if no redirection required - it does nothing,
  * unless the %force param is true.
  */
-static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid,
-                                     int ac, int ssn, unsigned int wdg_timeout,
-                                     bool force)
+static int iwl_mvm_redirect_queue(struct iwl_mvm *mvm, int queue, int tid,
+                                 int ac, int ssn, unsigned int wdg_timeout,
+                                 bool force, struct iwl_mvm_txq *txq)
 {
        struct iwl_scd_txq_cfg_cmd cmd = {
                .scd_queue = queue,
                .action = SCD_CFG_DISABLE_QUEUE,
        };
        bool shared_queue;
-       unsigned long mq;
        int ret;
 
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
@@ -695,14 +672,14 @@ static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid,
        cmd.sta_id = mvm->queue_info[queue].ra_sta_id;
        cmd.tx_fifo = iwl_mvm_ac_to_tx_fifo[mvm->queue_info[queue].mac80211_ac];
        cmd.tid = mvm->queue_info[queue].txq_tid;
-       mq = mvm->hw_queue_to_mac80211[queue];
        shared_queue = hweight16(mvm->queue_info[queue].tid_bitmap) > 1;
 
        IWL_DEBUG_TX_QUEUES(mvm, "Redirecting TXQ #%d to FIFO #%d\n",
                            queue, iwl_mvm_ac_to_tx_fifo[ac]);
 
-       /* Stop MAC queues and wait for this queue to empty */
-       iwl_mvm_stop_mac_queues(mvm, mq);
+       /* Stop the queue and wait for it to empty */
+       txq->stopped = true;
+
        ret = iwl_trans_wait_tx_queues_empty(mvm->trans, BIT(queue));
        if (ret) {
                IWL_ERR(mvm, "Error draining queue %d before reconfig\n",
@@ -743,8 +720,8 @@ static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid,
                iwl_trans_txq_set_shared_mode(mvm->trans, queue, true);
 
 out:
-       /* Continue using the MAC queues */
-       iwl_mvm_start_mac_queues(mvm, mq);
+       /* Continue using the queue */
+       txq->stopped = false;
 
        return ret;
 }
@@ -769,7 +746,7 @@ static int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id,
        return -ENOSPC;
 }
 
-static int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, int mac80211_queue,
+static int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm,
                                   u8 sta_id, u8 tid, unsigned int timeout)
 {
        int queue, size = IWL_DEFAULT_QUEUE_SIZE;
@@ -792,10 +769,7 @@ static int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, int mac80211_queue,
        IWL_DEBUG_TX_QUEUES(mvm, "Enabling TXQ #%d for sta %d tid %d\n",
                            queue, sta_id, tid);
 
-       mvm->hw_queue_to_mac80211[queue] |= BIT(mac80211_queue);
-       IWL_DEBUG_TX_QUEUES(mvm,
-                           "Enabling TXQ #%d (mac80211 map:0x%x)\n",
-                           queue, mvm->hw_queue_to_mac80211[queue]);
+       IWL_DEBUG_TX_QUEUES(mvm, "Enabling TXQ #%d\n", queue);
 
        return queue;
 }
@@ -805,9 +779,10 @@ static int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm,
                                        int tid)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       struct iwl_mvm_txq *mvmtxq =
+               iwl_mvm_txq_from_tid(sta, tid);
        unsigned int wdg_timeout =
                iwl_mvm_get_wd_timeout(mvm, mvmsta->vif, false, false);
-       u8 mac_queue = mvmsta->vif->hw_queue[ac];
        int queue = -1;
 
        lockdep_assert_held(&mvm->mutex);
@@ -815,11 +790,16 @@ static int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm,
        IWL_DEBUG_TX_QUEUES(mvm,
                            "Allocating queue for sta %d on tid %d\n",
                            mvmsta->sta_id, tid);
-       queue = iwl_mvm_tvqm_enable_txq(mvm, mac_queue, mvmsta->sta_id, tid,
-                                       wdg_timeout);
+       queue = iwl_mvm_tvqm_enable_txq(mvm, mvmsta->sta_id, tid, wdg_timeout);
        if (queue < 0)
                return queue;
 
+       if (sta) {
+               mvmtxq->txq_id = queue;
+               mvm->tvqm_info[queue].txq_tid = tid;
+               mvm->tvqm_info[queue].sta_id = mvmsta->sta_id;
+       }
+
        IWL_DEBUG_TX_QUEUES(mvm, "Allocated queue is %d\n", queue);
 
        spin_lock_bh(&mvmsta->lock);
@@ -829,8 +809,9 @@ static int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm,
        return 0;
 }
 
-static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue,
-                                      int mac80211_queue, u8 sta_id, u8 tid)
+static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm,
+                                      struct ieee80211_sta *sta,
+                                      int queue, u8 sta_id, u8 tid)
 {
        bool enable_queue = true;
 
@@ -845,14 +826,6 @@ static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue,
        if (mvm->queue_info[queue].tid_bitmap)
                enable_queue = false;
 
-       if (mac80211_queue != IEEE80211_INVAL_HW_QUEUE) {
-               WARN(mac80211_queue >=
-                    BITS_PER_BYTE * sizeof(mvm->hw_queue_to_mac80211[0]),
-                    "cannot track mac80211 queue %d (queue %d, sta %d, tid %d)\n",
-                    mac80211_queue, queue, sta_id, tid);
-               mvm->hw_queue_to_mac80211[queue] |= BIT(mac80211_queue);
-       }
-
        mvm->queue_info[queue].tid_bitmap |= BIT(tid);
        mvm->queue_info[queue].ra_sta_id = sta_id;
 
@@ -866,16 +839,22 @@ static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue,
                mvm->queue_info[queue].txq_tid = tid;
        }
 
+       if (sta) {
+               struct iwl_mvm_txq *mvmtxq =
+                       iwl_mvm_txq_from_tid(sta, tid);
+
+               mvmtxq->txq_id = queue;
+       }
+
        IWL_DEBUG_TX_QUEUES(mvm,
-                           "Enabling TXQ #%d tids=0x%x (mac80211 map:0x%x)\n",
-                           queue, mvm->queue_info[queue].tid_bitmap,
-                           mvm->hw_queue_to_mac80211[queue]);
+                           "Enabling TXQ #%d tids=0x%x\n",
+                           queue, mvm->queue_info[queue].tid_bitmap);
 
        return enable_queue;
 }
 
-static bool iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue,
-                              int mac80211_queue, u16 ssn,
+static bool iwl_mvm_enable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                              int queue, u16 ssn,
                               const struct iwl_trans_txq_scd_cfg *cfg,
                               unsigned int wdg_timeout)
 {
@@ -895,8 +874,7 @@ static bool iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue,
                return false;
 
        /* Send the enabling command if we need to */
-       if (!iwl_mvm_update_txq_mapping(mvm, queue, mac80211_queue,
-                                       cfg->sta_id, cfg->tid))
+       if (!iwl_mvm_update_txq_mapping(mvm, sta, queue, cfg->sta_id, cfg->tid))
                return false;
 
        inc_ssn = iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn,
@@ -989,9 +967,10 @@ static void iwl_mvm_unshare_queue(struct iwl_mvm *mvm, int queue)
 
        ssn = IEEE80211_SEQ_TO_SN(mvmsta->tid_data[tid].seq_number);
 
-       ret = iwl_mvm_scd_queue_redirect(mvm, queue, tid,
-                                        tid_to_mac80211_ac[tid], ssn,
-                                        wdg_timeout, true);
+       ret = iwl_mvm_redirect_queue(mvm, queue, tid,
+                                    tid_to_mac80211_ac[tid], ssn,
+                                    wdg_timeout, true,
+                                    iwl_mvm_txq_from_tid(sta, tid));
        if (ret) {
                IWL_ERR(mvm, "Failed to redirect TXQ %d\n", queue);
                return;
@@ -1068,11 +1047,9 @@ static bool iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm,
         * Remove the ones that did.
         */
        for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) {
-               int mac_queue = mvmsta->vif->hw_queue[tid_to_mac80211_ac[tid]];
                u16 tid_bitmap;
 
                mvmsta->tid_data[tid].txq_id = IWL_MVM_INVALID_QUEUE;
-               mvm->hw_queue_to_mac80211[queue] &= ~BIT(mac_queue);
                mvm->queue_info[queue].tid_bitmap &= ~BIT(tid);
 
                tid_bitmap = mvm->queue_info[queue].tid_bitmap;
@@ -1105,10 +1082,6 @@ static bool iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm,
         * sure all TIDs have existing corresponding mac queues enabled
         */
        tid_bitmap = mvm->queue_info[queue].tid_bitmap;
-       for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) {
-               mvm->hw_queue_to_mac80211[queue] |=
-                       BIT(mvmsta->vif->hw_queue[tid_to_mac80211_ac[tid]]);
-       }
 
        /* If the queue is marked as shared - "unshare" it */
        if (hweight16(mvm->queue_info[queue].tid_bitmap) == 1 &&
@@ -1136,6 +1109,7 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta)
        unsigned long unshare_queues = 0;
        unsigned long changetid_queues = 0;
        int i, ret, free_queue = -ENOSPC;
+       struct ieee80211_sta *queue_owner  = NULL;
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -1201,13 +1175,14 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta)
                                                   inactive_tid_bitmap,
                                                   &unshare_queues,
                                                   &changetid_queues);
-               if (ret >= 0 && free_queue < 0)
+               if (ret >= 0 && free_queue < 0) {
+                       queue_owner = sta;
                        free_queue = ret;
+               }
                /* only unlock sta lock - we still need the queue info lock */
                spin_unlock_bh(&mvmsta->lock);
        }
 
-       rcu_read_unlock();
 
        /* Reconfigure queues requiring reconfiguation */
        for_each_set_bit(i, &unshare_queues, IWL_MAX_HW_QUEUES)
@@ -1216,18 +1191,21 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta)
                iwl_mvm_change_queue_tid(mvm, i);
 
        if (free_queue >= 0 && alloc_for_sta != IWL_MVM_INVALID_STA) {
-               ret = iwl_mvm_free_inactive_queue(mvm, free_queue,
+               ret = iwl_mvm_free_inactive_queue(mvm, free_queue, queue_owner,
                                                  alloc_for_sta);
-               if (ret)
+               if (ret) {
+                       rcu_read_unlock();
                        return ret;
+               }
        }
 
+       rcu_read_unlock();
+
        return free_queue;
 }
 
 static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
-                                  struct ieee80211_sta *sta, u8 ac, int tid,
-                                  struct ieee80211_hdr *hdr)
+                                  struct ieee80211_sta *sta, u8 ac, int tid)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
        struct iwl_trans_txq_scd_cfg cfg = {
@@ -1238,7 +1216,6 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
        };
        unsigned int wdg_timeout =
                iwl_mvm_get_wd_timeout(mvm, mvmsta->vif, false, false);
-       u8 mac_queue = mvmsta->vif->hw_queue[ac];
        int queue = -1;
        unsigned long disable_agg_tids = 0;
        enum iwl_mvm_agg_state queue_state;
@@ -1257,12 +1234,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
        ssn = IEEE80211_SEQ_TO_SN(mvmsta->tid_data[tid].seq_number);
        spin_unlock_bh(&mvmsta->lock);
 
-       /*
-        * Non-QoS, QoS NDP and MGMT frames should go to a MGMT queue, if one
-        * exists
-        */
-       if (!ieee80211_is_data_qos(hdr->frame_control) ||
-           ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+       if (tid == IWL_MAX_TID_COUNT) {
                queue = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id,
                                                IWL_MVM_DQA_MIN_MGMT_QUEUE,
                                                IWL_MVM_DQA_MAX_MGMT_QUEUE);
@@ -1341,8 +1313,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
                }
        }
 
-       inc_ssn = iwl_mvm_enable_txq(mvm, queue, mac_queue,
-                                    ssn, &cfg, wdg_timeout);
+       inc_ssn = iwl_mvm_enable_txq(mvm, sta, queue, ssn, &cfg, wdg_timeout);
 
        /*
         * Mark queue as shared in transport if shared
@@ -1384,8 +1355,9 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
                }
        } else {
                /* Redirect queue, if needed */
-               ret = iwl_mvm_scd_queue_redirect(mvm, queue, tid, ac, ssn,
-                                                wdg_timeout, false);
+               ret = iwl_mvm_redirect_queue(mvm, queue, tid, ac, ssn,
+                                            wdg_timeout, false,
+                                            iwl_mvm_txq_from_tid(sta, tid));
                if (ret)
                        goto out_err;
        }
@@ -1393,7 +1365,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
        return 0;
 
 out_err:
-       iwl_mvm_disable_txq(mvm, queue, mac_queue, tid, 0);
+       iwl_mvm_disable_txq(mvm, sta, queue, tid, 0);
 
        return ret;
 }
@@ -1406,87 +1378,32 @@ static inline u8 iwl_mvm_tid_to_ac_queue(int tid)
        return tid_to_mac80211_ac[tid];
 }
 
-static void iwl_mvm_tx_deferred_stream(struct iwl_mvm *mvm,
-                                      struct ieee80211_sta *sta, int tid)
-{
-       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-       struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
-       struct sk_buff *skb;
-       struct ieee80211_hdr *hdr;
-       struct sk_buff_head deferred_tx;
-       u8 mac_queue;
-       bool no_queue = false; /* Marks if there is a problem with the queue */
-       u8 ac;
-
-       lockdep_assert_held(&mvm->mutex);
-
-       skb = skb_peek(&tid_data->deferred_tx_frames);
-       if (!skb)
-               return;
-       hdr = (void *)skb->data;
-
-       ac = iwl_mvm_tid_to_ac_queue(tid);
-       mac_queue = IEEE80211_SKB_CB(skb)->hw_queue;
-
-       if (tid_data->txq_id == IWL_MVM_INVALID_QUEUE &&
-           iwl_mvm_sta_alloc_queue(mvm, sta, ac, tid, hdr)) {
-               IWL_ERR(mvm,
-                       "Can't alloc TXQ for sta %d tid %d - dropping frame\n",
-                       mvmsta->sta_id, tid);
-
-               /*
-                * Mark queue as problematic so later the deferred traffic is
-                * freed, as we can do nothing with it
-                */
-               no_queue = true;
-       }
-
-       __skb_queue_head_init(&deferred_tx);
-
-       /* Disable bottom-halves when entering TX path */
-       local_bh_disable();
-       spin_lock(&mvmsta->lock);
-       skb_queue_splice_init(&tid_data->deferred_tx_frames, &deferred_tx);
-       mvmsta->deferred_traffic_tid_map &= ~BIT(tid);
-       spin_unlock(&mvmsta->lock);
-
-       while ((skb = __skb_dequeue(&deferred_tx)))
-               if (no_queue || iwl_mvm_tx_skb(mvm, skb, sta))
-                       ieee80211_free_txskb(mvm->hw, skb);
-       local_bh_enable();
-
-       /* Wake queue */
-       iwl_mvm_start_mac_queues(mvm, BIT(mac_queue));
-}
-
 void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk)
 {
        struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm,
                                           add_stream_wk);
-       struct ieee80211_sta *sta;
-       struct iwl_mvm_sta *mvmsta;
-       unsigned long deferred_tid_traffic;
-       int sta_id, tid;
 
        mutex_lock(&mvm->mutex);
 
        iwl_mvm_inactivity_check(mvm, IWL_MVM_INVALID_STA);
 
-       /* Go over all stations with deferred traffic */
-       for_each_set_bit(sta_id, mvm->sta_deferred_frames,
-                        IWL_MVM_STATION_COUNT) {
-               clear_bit(sta_id, mvm->sta_deferred_frames);
-               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
-                                               lockdep_is_held(&mvm->mutex));
-               if (IS_ERR_OR_NULL(sta))
-                       continue;
+       while (!list_empty(&mvm->add_stream_txqs)) {
+               struct iwl_mvm_txq *mvmtxq;
+               struct ieee80211_txq *txq;
+               u8 tid;
 
-               mvmsta = iwl_mvm_sta_from_mac80211(sta);
-               deferred_tid_traffic = mvmsta->deferred_traffic_tid_map;
+               mvmtxq = list_first_entry(&mvm->add_stream_txqs,
+                                         struct iwl_mvm_txq, list);
+
+               txq = container_of((void *)mvmtxq, struct ieee80211_txq,
+                                  drv_priv);
+               tid = txq->tid;
+               if (tid == IEEE80211_NUM_TIDS)
+                       tid = IWL_MAX_TID_COUNT;
 
-               for_each_set_bit(tid, &deferred_tid_traffic,
-                                IWL_MAX_TID_COUNT + 1)
-                       iwl_mvm_tx_deferred_stream(mvm, sta, tid);
+               iwl_mvm_sta_alloc_queue(mvm, txq->sta, txq->ac, tid);
+               list_del_init(&mvmtxq->list);
+               iwl_mvm_mac_itxq_xmit(mvm->hw, txq);
        }
 
        mutex_unlock(&mvm->mutex);
@@ -1542,10 +1459,11 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm,
  * Note that re-enabling aggregations isn't done in this function.
  */
 static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm,
-                                                struct iwl_mvm_sta *mvm_sta)
+                                                struct ieee80211_sta *sta)
 {
-       unsigned int wdg_timeout =
-                       iwl_mvm_get_wd_timeout(mvm, mvm_sta->vif, false, false);
+       struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+       unsigned int wdg =
+               iwl_mvm_get_wd_timeout(mvm, mvm_sta->vif, false, false);
        int i;
        struct iwl_trans_txq_scd_cfg cfg = {
                .sta_id = mvm_sta->sta_id,
@@ -1561,23 +1479,18 @@ static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm,
                struct iwl_mvm_tid_data *tid_data = &mvm_sta->tid_data[i];
                int txq_id = tid_data->txq_id;
                int ac;
-               u8 mac_queue;
 
                if (txq_id == IWL_MVM_INVALID_QUEUE)
                        continue;
 
-               skb_queue_head_init(&tid_data->deferred_tx_frames);
-
                ac = tid_to_mac80211_ac[i];
-               mac_queue = mvm_sta->vif->hw_queue[ac];
 
                if (iwl_mvm_has_new_tx_api(mvm)) {
                        IWL_DEBUG_TX_QUEUES(mvm,
                                            "Re-mapping sta %d tid %d\n",
                                            mvm_sta->sta_id, i);
-                       txq_id = iwl_mvm_tvqm_enable_txq(mvm, mac_queue,
-                                                        mvm_sta->sta_id,
-                                                        i, wdg_timeout);
+                       txq_id = iwl_mvm_tvqm_enable_txq(mvm, mvm_sta->sta_id,
+                                                        i, wdg);
                        tid_data->txq_id = txq_id;
 
                        /*
@@ -1600,8 +1513,7 @@ static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm,
                                            "Re-mapping sta %d tid %d to queue %d\n",
                                            mvm_sta->sta_id, i, txq_id);
 
-                       iwl_mvm_enable_txq(mvm, txq_id, mac_queue, seq, &cfg,
-                                          wdg_timeout);
+                       iwl_mvm_enable_txq(mvm, sta, txq_id, seq, &cfg, wdg);
                        mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_READY;
                }
        }
@@ -1691,7 +1603,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
                if (ret)
                        goto err;
 
-               iwl_mvm_realloc_queues_after_restart(mvm, mvm_sta);
+               iwl_mvm_realloc_queues_after_restart(mvm, sta);
                sta_update = true;
                sta_flags = iwl_mvm_has_new_tx_api(mvm) ? 0 : STA_MODIFY_QUEUES;
                goto update_fw;
@@ -1724,9 +1636,17 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
                 * frames until the queue is allocated
                 */
                mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE;
-               skb_queue_head_init(&mvm_sta->tid_data[i].deferred_tx_frames);
        }
-       mvm_sta->deferred_traffic_tid_map = 0;
+
+       for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+               struct iwl_mvm_txq *mvmtxq =
+                       iwl_mvm_txq_from_mac80211(sta->txq[i]);
+
+               mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;
+               INIT_LIST_HEAD(&mvmtxq->list);
+               atomic_set(&mvmtxq->tx_request, 0);
+       }
+
        mvm_sta->agg_tids = 0;
 
        if (iwl_mvm_has_new_rx_api(mvm) &&
@@ -1861,9 +1781,9 @@ static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id)
 
 static void iwl_mvm_disable_sta_queues(struct iwl_mvm *mvm,
                                       struct ieee80211_vif *vif,
-                                      struct iwl_mvm_sta *mvm_sta)
+                                      struct ieee80211_sta *sta)
 {
-       int ac;
+       struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
        int i;
 
        lockdep_assert_held(&mvm->mutex);
@@ -1872,11 +1792,17 @@ static void iwl_mvm_disable_sta_queues(struct iwl_mvm *mvm,
                if (mvm_sta->tid_data[i].txq_id == IWL_MVM_INVALID_QUEUE)
                        continue;
 
-               ac = iwl_mvm_tid_to_ac_queue(i);
-               iwl_mvm_disable_txq(mvm, mvm_sta->tid_data[i].txq_id,
-                                   vif->hw_queue[ac], i, 0);
+               iwl_mvm_disable_txq(mvm, sta, mvm_sta->tid_data[i].txq_id, i,
+                                   0);
                mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE;
        }
+
+       for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+               struct iwl_mvm_txq *mvmtxq =
+                       iwl_mvm_txq_from_mac80211(sta->txq[i]);
+
+               mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE;
+       }
 }
 
 int iwl_mvm_wait_sta_queues_empty(struct iwl_mvm *mvm,
@@ -1938,7 +1864,7 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
 
        ret = iwl_mvm_drain_sta(mvm, mvm_sta, false);
 
-       iwl_mvm_disable_sta_queues(mvm, vif, mvm_sta);
+       iwl_mvm_disable_sta_queues(mvm, vif, sta);
 
        /* If there is a TXQ still marked as reserved - free it */
        if (mvm_sta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) {
@@ -2044,7 +1970,7 @@ static void iwl_mvm_enable_aux_snif_queue(struct iwl_mvm *mvm, u16 *queue,
 
        if (iwl_mvm_has_new_tx_api(mvm)) {
                int tvqm_queue =
-                       iwl_mvm_tvqm_enable_txq(mvm, *queue, sta_id,
+                       iwl_mvm_tvqm_enable_txq(mvm, sta_id,
                                                IWL_MAX_TID_COUNT,
                                                wdg_timeout);
                *queue = tvqm_queue;
@@ -2057,7 +1983,7 @@ static void iwl_mvm_enable_aux_snif_queue(struct iwl_mvm *mvm, u16 *queue,
                        .frame_limit = IWL_FRAME_LIMIT,
                };
 
-               iwl_mvm_enable_txq(mvm, *queue, *queue, 0, &cfg, wdg_timeout);
+               iwl_mvm_enable_txq(mvm, NULL, *queue, 0, &cfg, wdg_timeout);
        }
 }
 
@@ -2135,8 +2061,7 @@ int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 
        lockdep_assert_held(&mvm->mutex);
 
-       iwl_mvm_disable_txq(mvm, mvm->snif_queue, mvm->snif_queue,
-                           IWL_MAX_TID_COUNT, 0);
+       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)
                IWL_WARN(mvm, "Failed sending remove station\n");
@@ -2195,8 +2120,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 
                bsta->tfd_queue_msk |= BIT(queue);
 
-               iwl_mvm_enable_txq(mvm, queue, vif->hw_queue[0], 0,
-                                  &cfg, wdg_timeout);
+               iwl_mvm_enable_txq(mvm, NULL, queue, 0, &cfg, wdg_timeout);
        }
 
        if (vif->type == NL80211_IFTYPE_ADHOC)
@@ -2215,8 +2139,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
         * to firmware so enable queue here - after the station was added
         */
        if (iwl_mvm_has_new_tx_api(mvm)) {
-               queue = iwl_mvm_tvqm_enable_txq(mvm, vif->hw_queue[0],
-                                               bsta->sta_id,
+               queue = iwl_mvm_tvqm_enable_txq(mvm, bsta->sta_id,
                                                IWL_MAX_TID_COUNT,
                                                wdg_timeout);
 
@@ -2254,7 +2177,7 @@ static void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm,
                return;
        }
 
-       iwl_mvm_disable_txq(mvm, queue, vif->hw_queue[0], IWL_MAX_TID_COUNT, 0);
+       iwl_mvm_disable_txq(mvm, NULL, queue, IWL_MAX_TID_COUNT, 0);
        if (iwl_mvm_has_new_tx_api(mvm))
                return;
 
@@ -2377,10 +2300,8 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
         * Note that this is done here as we want to avoid making DQA
         * changes in mac80211 layer.
         */
-       if (vif->type == NL80211_IFTYPE_ADHOC) {
-               vif->cab_queue = IWL_MVM_DQA_GCAST_QUEUE;
-               mvmvif->cab_queue = vif->cab_queue;
-       }
+       if (vif->type == NL80211_IFTYPE_ADHOC)
+               mvmvif->cab_queue = IWL_MVM_DQA_GCAST_QUEUE;
 
        /*
         * While in previous FWs we had to exclude cab queue from TFD queue
@@ -2388,9 +2309,9 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
         */
        if (!iwl_mvm_has_new_tx_api(mvm) &&
            fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) {
-               iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, 0,
-                                  &cfg, timeout);
-               msta->tfd_queue_msk |= BIT(vif->cab_queue);
+               iwl_mvm_enable_txq(mvm, NULL, mvmvif->cab_queue, 0, &cfg,
+                                  timeout);
+               msta->tfd_queue_msk |= BIT(mvmvif->cab_queue);
        }
        ret = iwl_mvm_add_int_sta_common(mvm, msta, maddr,
                                         mvmvif->id, mvmvif->color);
@@ -2407,15 +2328,14 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
         * tfd_queue_mask.
         */
        if (iwl_mvm_has_new_tx_api(mvm)) {
-               int queue = iwl_mvm_tvqm_enable_txq(mvm, vif->cab_queue,
-                                                   msta->sta_id,
+               int queue = iwl_mvm_tvqm_enable_txq(mvm, msta->sta_id,
                                                    0,
                                                    timeout);
                mvmvif->cab_queue = queue;
        } else if (!fw_has_api(&mvm->fw->ucode_capa,
                               IWL_UCODE_TLV_API_STA_TYPE))
-               iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, 0,
-                                  &cfg, timeout);
+               iwl_mvm_enable_txq(mvm, NULL, mvmvif->cab_queue, 0, &cfg,
+                                  timeout);
 
        if (mvmvif->ap_wep_key) {
                u8 key_offset = iwl_mvm_set_fw_key_idx(mvm);
@@ -2446,8 +2366,7 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 
        iwl_mvm_flush_sta(mvm, &mvmvif->mcast_sta, true, 0);
 
-       iwl_mvm_disable_txq(mvm, mvmvif->cab_queue, vif->cab_queue,
-                           0, 0);
+       iwl_mvm_disable_txq(mvm, NULL, mvmvif->cab_queue, 0, 0);
 
        ret = iwl_mvm_rm_sta_common(mvm, mvmvif->mcast_sta.sta_id);
        if (ret)
@@ -2781,7 +2700,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
        struct iwl_mvm_tid_data *tid_data;
        u16 normalized_ssn;
-       int txq_id;
+       u16 txq_id;
        int ret;
 
        if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
@@ -2823,17 +2742,24 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
         */
        txq_id = mvmsta->tid_data[tid].txq_id;
        if (txq_id == IWL_MVM_INVALID_QUEUE) {
-               txq_id = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id,
-                                                IWL_MVM_DQA_MIN_DATA_QUEUE,
-                                                IWL_MVM_DQA_MAX_DATA_QUEUE);
-               if (txq_id < 0) {
-                       ret = txq_id;
+               ret = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id,
+                                             IWL_MVM_DQA_MIN_DATA_QUEUE,
+                                             IWL_MVM_DQA_MAX_DATA_QUEUE);
+               if (ret < 0) {
                        IWL_ERR(mvm, "Failed to allocate agg queue\n");
                        goto out;
                }
 
+               txq_id = ret;
+
                /* TXQ hasn't yet been enabled, so mark it only as reserved */
                mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_RESERVED;
+       } else if (WARN_ON(txq_id >= IWL_MAX_HW_QUEUES)) {
+               ret = -ENXIO;
+               IWL_ERR(mvm, "tid_id %d out of range (0, %d)!\n",
+                       tid, IWL_MAX_HW_QUEUES - 1);
+               goto out;
+
        } else if (unlikely(mvm->queue_info[txq_id].status ==
                            IWL_MVM_QUEUE_SHARED)) {
                ret = -ENXIO;
@@ -2976,8 +2902,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        }
 
        if (alloc_queue)
-               iwl_mvm_enable_txq(mvm, queue,
-                                  vif->hw_queue[tid_to_mac80211_ac[tid]], ssn,
+               iwl_mvm_enable_txq(mvm, sta, queue, ssn,
                                   &cfg, wdg_timeout);
 
        /* Send ADD_STA command to enable aggs only if the queue isn't shared */
index d52cd88..0614296 100644 (file)
@@ -297,7 +297,6 @@ enum iwl_mvm_agg_state {
 
 /**
  * struct iwl_mvm_tid_data - holds the states for each RA / TID
- * @deferred_tx_frames: deferred TX frames for this RA/TID
  * @seq_number: the next WiFi sequence number to use
  * @next_reclaimed: the WiFi sequence number of the next packet to be acked.
  *     This is basically (last acked packet++).
@@ -318,7 +317,6 @@ enum iwl_mvm_agg_state {
  *      tpt_meas_start
  */
 struct iwl_mvm_tid_data {
-       struct sk_buff_head deferred_tx_frames;
        u16 seq_number;
        u16 next_reclaimed;
        /* The rest is Tx AGG related */
@@ -427,8 +425,6 @@ struct iwl_mvm_sta {
        struct iwl_mvm_key_pn __rcu *ptk_pn[4];
        struct iwl_mvm_rxq_dup_data *dup_data;
 
-       u16 deferred_traffic_tid_map;
-
        u8 reserved_queue;
 
        /* Temporary, until the new TLC will control the Tx protection */
index e02f4eb..859aa5a 100644 (file)
@@ -399,6 +399,9 @@ iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,
        struct ieee80211_tx_info *info;
        struct ieee80211_hdr *hdr;
        struct iwl_tdls_channel_switch_cmd cmd = {0};
+       struct iwl_tdls_channel_switch_cmd_tail *tail =
+               iwl_mvm_chan_info_cmd_tail(mvm, &cmd.ci);
+       u16 len = sizeof(cmd) - iwl_mvm_chan_info_padding(mvm);
        int ret;
 
        lockdep_assert_held(&mvm->mutex);
@@ -414,9 +417,9 @@ iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,
        }
 
        cmd.switch_type = type;
-       cmd.timing.frame_timestamp = cpu_to_le32(timestamp);
-       cmd.timing.switch_time = cpu_to_le32(switch_time);
-       cmd.timing.switch_timeout = cpu_to_le32(switch_timeout);
+       tail->timing.frame_timestamp = cpu_to_le32(timestamp);
+       tail->timing.switch_time = cpu_to_le32(switch_time);
+       tail->timing.switch_timeout = cpu_to_le32(switch_timeout);
 
        rcu_read_lock();
        sta = ieee80211_find_sta(vif, peer);
@@ -448,21 +451,16 @@ iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,
                }
        }
 
-       if (chandef) {
-               cmd.ci.band = (chandef->chan->band == NL80211_BAND_2GHZ ?
-                              PHY_BAND_24 : PHY_BAND_5);
-               cmd.ci.channel = chandef->chan->hw_value;
-               cmd.ci.width = iwl_mvm_get_channel_width(chandef);
-               cmd.ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
-       }
+       if (chandef)
+               iwl_mvm_set_chan_info_chandef(mvm, &cmd.ci, chandef);
 
        /* keep quota calculation simple for now - 50% of DTIM for TDLS */
-       cmd.timing.max_offchan_duration =
+       tail->timing.max_offchan_duration =
                        cpu_to_le32(TU_TO_US(vif->bss_conf.dtim_period *
                                             vif->bss_conf.beacon_int) / 2);
 
        /* Switch time is the first element in the switch-timing IE. */
-       cmd.frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2);
+       tail->frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2);
 
        info = IEEE80211_SKB_CB(skb);
        hdr = (void *)skb->data;
@@ -472,20 +470,19 @@ iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,
                        ret = -EINVAL;
                        goto out;
                }
-               iwl_mvm_set_tx_cmd_ccmp(info, &cmd.frame.tx_cmd);
+               iwl_mvm_set_tx_cmd_ccmp(info, &tail->frame.tx_cmd);
        }
 
-       iwl_mvm_set_tx_cmd(mvm, skb, &cmd.frame.tx_cmd, info,
+       iwl_mvm_set_tx_cmd(mvm, skb, &tail->frame.tx_cmd, info,
                           mvmsta->sta_id);
 
-       iwl_mvm_set_tx_cmd_rate(mvm, &cmd.frame.tx_cmd, info, sta,
+       iwl_mvm_set_tx_cmd_rate(mvm, &tail->frame.tx_cmd, info, sta,
                                hdr->frame_control);
        rcu_read_unlock();
 
-       memcpy(cmd.frame.data, skb->data, skb->len);
+       memcpy(tail->frame.data, skb->data, skb->len);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, TDLS_CHANNEL_SWITCH_CMD, 0,
-                                  sizeof(cmd), &cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, TDLS_CHANNEL_SWITCH_CMD, 0, len, &cmd);
        if (ret) {
                IWL_ERR(mvm, "Failed to send TDLS_CHANNEL_SWITCH cmd: %d\n",
                        ret);
index e1a6f4e..5b34100 100644 (file)
@@ -334,6 +334,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
                switch (te_data->vif->type) {
                case NL80211_IFTYPE_P2P_DEVICE:
                        ieee80211_remain_on_channel_expired(mvm->hw);
+                       set_bit(IWL_MVM_STATUS_NEED_FLUSH_P2P, &mvm->status);
                        iwl_mvm_roc_finished(mvm);
                        break;
                case NL80211_IFTYPE_STATION:
@@ -686,6 +687,8 @@ static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm,
                                      struct iwl_mvm_time_event_data *te_data)
 {
        struct iwl_hs20_roc_req aux_cmd = {};
+       u16 len = sizeof(aux_cmd) - iwl_mvm_chan_info_padding(mvm);
+
        u32 uid;
        int ret;
 
@@ -699,7 +702,7 @@ static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm,
        IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n",
                     le32_to_cpu(aux_cmd.event_unique_id));
        ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0,
-                                  sizeof(aux_cmd), &aux_cmd);
+                                  len, &aux_cmd);
 
        if (WARN_ON(ret))
                return;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.c b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c
deleted file mode 100644 (file)
index 01e0a99..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license.  When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <linuxwifi@intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2015 Intel Deutschland GmbH
- * All rights reserved.
- *
- * 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.
- *  * Neither the name Intel Corporation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * 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 MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * 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 DAMAGE.
- *
- *****************************************************************************/
-#include "mvm.h"
-#include "fw/api/tof.h"
-
-#define IWL_MVM_TOF_RANGE_REQ_MAX_ID 256
-
-void iwl_mvm_tof_init(struct iwl_mvm *mvm)
-{
-       struct iwl_mvm_tof_data *tof_data = &mvm->tof_data;
-
-       if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
-               return;
-
-       memset(tof_data, 0, sizeof(*tof_data));
-
-       tof_data->tof_cfg.sub_grp_cmd_id = cpu_to_le32(TOF_CONFIG_CMD);
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       if (IWL_MVM_TOF_IS_RESPONDER) {
-               tof_data->responder_cfg.sub_grp_cmd_id =
-                       cpu_to_le32(TOF_RESPONDER_CONFIG_CMD);
-               tof_data->responder_cfg.sta_id = IWL_MVM_INVALID_STA;
-       }
-#endif
-
-       tof_data->range_req.sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_REQ_CMD);
-       tof_data->range_req.req_timeout = 1;
-       tof_data->range_req.initiator = 1;
-       tof_data->range_req.report_policy = 3;
-
-       tof_data->range_req_ext.sub_grp_cmd_id =
-               cpu_to_le32(TOF_RANGE_REQ_EXT_CMD);
-
-       mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
-       mvm->init_status |= IWL_MVM_INIT_STATUS_TOF_INIT_COMPLETE;
-}
-
-void iwl_mvm_tof_clean(struct iwl_mvm *mvm)
-{
-       struct iwl_mvm_tof_data *tof_data = &mvm->tof_data;
-
-       if (!fw_has_capa(&mvm->fw->ucode_capa,
-                        IWL_UCODE_TLV_CAPA_TOF_SUPPORT) ||
-           !(mvm->init_status & IWL_MVM_INIT_STATUS_TOF_INIT_COMPLETE))
-               return;
-
-       memset(tof_data, 0, sizeof(*tof_data));
-       mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
-       mvm->init_status &= ~IWL_MVM_INIT_STATUS_TOF_INIT_COMPLETE;
-}
-
-static void iwl_tof_iterator(void *_data, u8 *mac,
-                            struct ieee80211_vif *vif)
-{
-       bool *enabled = _data;
-
-       /* non bss vif exists */
-       if (ieee80211_vif_type_p2p(vif) !=  NL80211_IFTYPE_STATION)
-               *enabled = false;
-}
-
-int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm)
-{
-       struct iwl_tof_config_cmd *cmd = &mvm->tof_data.tof_cfg;
-       bool enabled;
-
-       lockdep_assert_held(&mvm->mutex);
-
-       if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
-               return -EINVAL;
-
-       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
-                                                  IEEE80211_IFACE_ITER_NORMAL,
-                                                  iwl_tof_iterator, &enabled);
-       if (!enabled) {
-               IWL_DEBUG_INFO(mvm, "ToF is not supported (non bss vif)\n");
-               return -EINVAL;
-       }
-
-       mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
-       return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
-                                                   IWL_ALWAYS_LONG_GROUP, 0),
-                                   0, sizeof(*cmd), cmd);
-}
-
-int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id)
-{
-       struct iwl_tof_range_abort_cmd cmd = {
-               .sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_ABORT_CMD),
-               .request_id = id,
-       };
-
-       lockdep_assert_held(&mvm->mutex);
-
-       if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
-               return -EINVAL;
-
-       if (id != mvm->tof_data.active_range_request) {
-               IWL_ERR(mvm, "Invalid range request id %d (active %d)\n",
-                       id, mvm->tof_data.active_range_request);
-               return -EINVAL;
-       }
-
-       /* after abort is sent there's no active request anymore */
-       mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
-
-       return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
-                                                   IWL_ALWAYS_LONG_GROUP, 0),
-                                   0, sizeof(cmd), &cmd);
-}
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm,
-                             struct ieee80211_vif *vif)
-{
-       struct iwl_tof_responder_config_cmd *cmd = &mvm->tof_data.responder_cfg;
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
-       lockdep_assert_held(&mvm->mutex);
-
-       if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
-               return -EINVAL;
-
-       if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
-           !mvmvif->ap_ibss_active) {
-               IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
-               return -EIO;
-       }
-
-       cmd->sta_id = mvmvif->bcast_sta.sta_id;
-       memcpy(cmd->bssid, vif->addr, ETH_ALEN);
-       return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
-                                                   IWL_ALWAYS_LONG_GROUP, 0),
-                                   0, sizeof(*cmd), cmd);
-}
-#endif
-
-int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm,
-                                 struct ieee80211_vif *vif)
-{
-       struct iwl_host_cmd cmd = {
-               .id = iwl_cmd_id(TOF_CMD, IWL_ALWAYS_LONG_GROUP, 0),
-               .len = { sizeof(mvm->tof_data.range_req), },
-               /* no copy because of the command size */
-               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-       };
-
-       lockdep_assert_held(&mvm->mutex);
-
-       if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
-               return -EINVAL;
-
-       if (ieee80211_vif_type_p2p(vif) !=  NL80211_IFTYPE_STATION) {
-               IWL_ERR(mvm, "Cannot send range request, not STA mode\n");
-               return -EIO;
-       }
-
-       /* nesting of range requests is not supported in FW */
-       if (mvm->tof_data.active_range_request !=
-               IWL_MVM_TOF_RANGE_REQ_MAX_ID) {
-               IWL_ERR(mvm, "Cannot send range req, already active req %d\n",
-                       mvm->tof_data.active_range_request);
-               return -EIO;
-       }
-
-       mvm->tof_data.active_range_request = mvm->tof_data.range_req.request_id;
-
-       cmd.data[0] = &mvm->tof_data.range_req;
-       return iwl_mvm_send_cmd(mvm, &cmd);
-}
-
-int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm,
-                                     struct ieee80211_vif *vif)
-{
-       lockdep_assert_held(&mvm->mutex);
-
-       if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT))
-               return -EINVAL;
-
-       if (ieee80211_vif_type_p2p(vif) !=  NL80211_IFTYPE_STATION) {
-               IWL_ERR(mvm, "Cannot send ext range req, not in STA mode\n");
-               return -EIO;
-       }
-
-       return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD,
-                                                   IWL_ALWAYS_LONG_GROUP, 0),
-                                   0, sizeof(mvm->tof_data.range_req_ext),
-                                   &mvm->tof_data.range_req_ext);
-}
-
-static int iwl_mvm_tof_range_resp(struct iwl_mvm *mvm, void *data)
-{
-       struct iwl_tof_range_rsp_ntfy *resp = (void *)data;
-
-       if (resp->request_id != mvm->tof_data.active_range_request) {
-               IWL_ERR(mvm, "Request id mismatch, got %d, active %d\n",
-                       resp->request_id, mvm->tof_data.active_range_request);
-               return -EIO;
-       }
-
-       memcpy(&mvm->tof_data.range_resp, resp,
-              sizeof(struct iwl_tof_range_rsp_ntfy));
-       mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID;
-
-       return 0;
-}
-
-static int iwl_mvm_tof_mcsi_notif(struct iwl_mvm *mvm, void *data)
-{
-       struct iwl_tof_mcsi_notif *resp = (struct iwl_tof_mcsi_notif *)data;
-
-       IWL_DEBUG_INFO(mvm, "MCSI notification, token %d\n", resp->token);
-       return 0;
-}
-
-static int iwl_mvm_tof_nb_report_notif(struct iwl_mvm *mvm, void *data)
-{
-       struct iwl_tof_neighbor_report *report =
-               (struct iwl_tof_neighbor_report *)data;
-
-       IWL_DEBUG_INFO(mvm, "NB report, bssid %pM, token %d, status 0x%x\n",
-                      report->bssid, report->request_token, report->status);
-       return 0;
-}
-
-void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm,
-                             struct iwl_rx_cmd_buffer *rxb)
-{
-       struct iwl_rx_packet *pkt = rxb_addr(rxb);
-       struct iwl_tof_gen_resp_cmd *resp = (void *)pkt->data;
-
-       lockdep_assert_held(&mvm->mutex);
-
-       switch (le32_to_cpu(resp->sub_grp_cmd_id)) {
-       case TOF_RANGE_RESPONSE_NOTIF:
-               iwl_mvm_tof_range_resp(mvm, resp->data);
-               break;
-       case TOF_MCSI_DEBUG_NOTIF:
-               iwl_mvm_tof_mcsi_notif(mvm, resp->data);
-               break;
-       case TOF_NEIGHBOR_REPORT_RSP_NOTIF:
-               iwl_mvm_tof_nb_report_notif(mvm, resp->data);
-               break;
-       default:
-              IWL_ERR(mvm, "Unknown sub-group command 0x%x\n",
-                      resp->sub_grp_cmd_id);
-              break;
-       }
-}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h
deleted file mode 100644 (file)
index 8138d06..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license.  When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2015 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <linuxwifi@intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2015 Intel Deutschland GmbH
- * All rights reserved.
- *
- * 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.
- *  * Neither the name Intel Corporation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * 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 MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * 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 DAMAGE.
- *
- *****************************************************************************/
-#ifndef __tof_h__
-#define __tof_h__
-
-#include "fw/api/tof.h"
-
-struct iwl_mvm_tof_data {
-       struct iwl_tof_config_cmd tof_cfg;
-       struct iwl_tof_range_req_cmd range_req;
-       struct iwl_tof_range_req_ext_cmd range_req_ext;
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       struct iwl_tof_responder_config_cmd responder_cfg;
-#endif
-       struct iwl_tof_range_rsp_ntfy range_resp;
-       u8 last_abort_id;
-       u16 active_range_request;
-};
-
-void iwl_mvm_tof_init(struct iwl_mvm *mvm);
-void iwl_mvm_tof_clean(struct iwl_mvm *mvm);
-int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm);
-int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id);
-int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm,
-                                 struct ieee80211_vif *vif);
-void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm,
-                             struct iwl_rx_cmd_buffer *rxb);
-int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm,
-                                     struct ieee80211_vif *vif);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm,
-                             struct ieee80211_vif *vif);
-#endif
-#endif /* __tof_h__ */
index 995fe2a..ac62eb8 100644 (file)
@@ -533,10 +533,11 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
 
                /*
                 * For data packets rate info comes from the fw. Only
-                * set rate/antenna during connection establishment.
+                * set rate/antenna during connection establishment or in case
+                * no station is given.
                 */
-               if (sta && (!ieee80211_is_data(hdr->frame_control) ||
-                           mvmsta->sta_state < IEEE80211_STA_AUTHORIZED)) {
+               if (!sta || !ieee80211_is_data(hdr->frame_control) ||
+                   mvmsta->sta_state < IEEE80211_STA_AUTHORIZED) {
                        flags |= IWL_TX_FLAGS_CMD_RATE;
                        rate_n_flags =
                                iwl_mvm_get_tx_rate_n_flags(mvm, info, sta,
@@ -602,11 +603,12 @@ static void iwl_mvm_skb_prepare_status(struct sk_buff *skb,
 }
 
 static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,
-                                     struct ieee80211_tx_info *info, __le16 fc)
+                                     struct ieee80211_tx_info *info,
+                                     struct ieee80211_hdr *hdr)
 {
-       struct iwl_mvm_vif *mvmvif;
-
-       mvmvif = iwl_mvm_vif_from_mac80211(info->control.vif);
+       struct iwl_mvm_vif *mvmvif =
+               iwl_mvm_vif_from_mac80211(info->control.vif);
+       __le16 fc = hdr->frame_control;
 
        switch (info->control.vif->type) {
        case NL80211_IFTYPE_AP:
@@ -625,7 +627,9 @@ static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,
                    (!ieee80211_is_bufferable_mmpdu(fc) ||
                     ieee80211_is_deauth(fc) || ieee80211_is_disassoc(fc)))
                        return mvm->probe_queue;
-               if (info->hw_queue == info->control.vif->cab_queue)
+
+               if (!ieee80211_has_order(fc) && !ieee80211_is_probe_req(fc) &&
+                   is_multicast_ether_addr(hdr->addr1))
                        return mvmvif->cab_queue;
 
                WARN_ONCE(info->control.vif->type != NL80211_IFTYPE_ADHOC,
@@ -634,8 +638,6 @@ static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,
        case NL80211_IFTYPE_P2P_DEVICE:
                if (ieee80211_is_mgmt(fc))
                        return mvm->p2p_dev_queue;
-               if (info->hw_queue == info->control.vif->cab_queue)
-                       return mvmvif->cab_queue;
 
                WARN_ON_ONCE(1);
                return mvm->p2p_dev_queue;
@@ -713,6 +715,8 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
        u8 sta_id;
        int hdrlen = ieee80211_hdrlen(hdr->frame_control);
        __le16 fc = hdr->frame_control;
+       bool offchannel = IEEE80211_SKB_CB(skb)->flags &
+               IEEE80211_TX_CTL_TX_OFFCHAN;
        int queue = -1;
 
        memcpy(&info, skb->cb, sizeof(info));
@@ -720,11 +724,6 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
        if (WARN_ON_ONCE(info.flags & IEEE80211_TX_CTL_AMPDU))
                return -1;
 
-       if (WARN_ON_ONCE(info.flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM &&
-                        (!info.control.vif ||
-                         info.hw_queue != info.control.vif->cab_queue)))
-               return -1;
-
        if (info.control.vif) {
                struct iwl_mvm_vif *mvmvif =
                        iwl_mvm_vif_from_mac80211(info.control.vif);
@@ -737,14 +736,12 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
                        else
                                sta_id = mvmvif->mcast_sta.sta_id;
 
-                       queue = iwl_mvm_get_ctrl_vif_queue(mvm, &info,
-                                                          hdr->frame_control);
-
+                       queue = iwl_mvm_get_ctrl_vif_queue(mvm, &info, hdr);
                } else if (info.control.vif->type == NL80211_IFTYPE_MONITOR) {
                        queue = mvm->snif_queue;
                        sta_id = mvm->snif_sta.sta_id;
                } else if (info.control.vif->type == NL80211_IFTYPE_STATION &&
-                          info.hw_queue == IWL_MVM_OFFCHANNEL_QUEUE) {
+                          offchannel) {
                        /*
                         * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets
                         * that can be used in 2 different types of vifs, P2P &
@@ -758,8 +755,10 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
                }
        }
 
-       if (queue < 0)
+       if (queue < 0) {
+               IWL_ERR(mvm, "No queue was found. Dropping TX\n");
                return -1;
+       }
 
        if (unlikely(ieee80211_is_probe_resp(fc)))
                iwl_mvm_probe_resp_set_noa(mvm, skb);
@@ -781,6 +780,35 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
        return 0;
 }
 
+unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm,
+                                   struct ieee80211_sta *sta, unsigned int tid)
+{
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       enum nl80211_band band = mvmsta->vif->bss_conf.chandef.chan->band;
+       u8 ac = tid_to_mac80211_ac[tid];
+       unsigned int txf;
+       int lmac = IWL_LMAC_24G_INDEX;
+
+       if (iwl_mvm_is_cdb_supported(mvm) &&
+           band == NL80211_BAND_5GHZ)
+               lmac = IWL_LMAC_5G_INDEX;
+
+       /* For HE redirect to trigger based fifos */
+       if (sta->he_cap.has_he && !WARN_ON(!iwl_mvm_has_new_tx_api(mvm)))
+               ac += 4;
+
+       txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, ac);
+
+       /*
+        * Don't send an AMSDU that will be longer than the TXF.
+        * Add a security margin of 256 for the TX command + headers.
+        * We also want to have the start of the next packet inside the
+        * fifo to be able to send bursts.
+        */
+       return min_t(unsigned int, mvmsta->max_amsdu_len,
+                    mvm->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256);
+}
+
 #ifdef CONFIG_INET
 
 static int
@@ -850,36 +878,6 @@ iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,
        return 0;
 }
 
-static unsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm,
-                                          struct ieee80211_sta *sta,
-                                          unsigned int tid)
-{
-       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-       enum nl80211_band band = mvmsta->vif->bss_conf.chandef.chan->band;
-       u8 ac = tid_to_mac80211_ac[tid];
-       unsigned int txf;
-       int lmac = IWL_LMAC_24G_INDEX;
-
-       if (iwl_mvm_is_cdb_supported(mvm) &&
-           band == NL80211_BAND_5GHZ)
-               lmac = IWL_LMAC_5G_INDEX;
-
-       /* For HE redirect to trigger based fifos */
-       if (sta->he_cap.has_he && !WARN_ON(!iwl_mvm_has_new_tx_api(mvm)))
-               ac += 4;
-
-       txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, ac);
-
-       /*
-        * Don't send an AMSDU that will be longer than the TXF.
-        * Add a security margin of 256 for the TX command + headers.
-        * We also want to have the start of the next packet inside the
-        * fifo to be able to send bursts.
-        */
-       return min_t(unsigned int, mvmsta->max_amsdu_len,
-                    mvm->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256);
-}
-
 static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
                          struct ieee80211_tx_info *info,
                          struct ieee80211_sta *sta,
@@ -1002,34 +1000,6 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
 }
 #endif
 
-static void iwl_mvm_tx_add_stream(struct iwl_mvm *mvm,
-                                 struct iwl_mvm_sta *mvm_sta, u8 tid,
-                                 struct sk_buff *skb)
-{
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       u8 mac_queue = info->hw_queue;
-       struct sk_buff_head *deferred_tx_frames;
-
-       lockdep_assert_held(&mvm_sta->lock);
-
-       mvm_sta->deferred_traffic_tid_map |= BIT(tid);
-       set_bit(mvm_sta->sta_id, mvm->sta_deferred_frames);
-
-       deferred_tx_frames = &mvm_sta->tid_data[tid].deferred_tx_frames;
-
-       skb_queue_tail(deferred_tx_frames, skb);
-
-       /*
-        * The first deferred frame should've stopped the MAC queues, so we
-        * should never get a second deferred frame for the RA/TID.
-        * In case of GSO the first packet may have been split, so don't warn.
-        */
-       if (skb_queue_len(deferred_tx_frames) == 1) {
-               iwl_mvm_stop_mac_queues(mvm, BIT(mac_queue));
-               schedule_work(&mvm->add_stream_wk);
-       }
-}
-
 /* Check if there are any timed-out TIDs on a given shared TXQ */
 static bool iwl_mvm_txq_should_update(struct iwl_mvm *mvm, int txq_id)
 {
@@ -1054,7 +1024,12 @@ static void iwl_mvm_tx_airtime(struct iwl_mvm *mvm,
                               int airtime)
 {
        int mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK;
-       struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[mac];
+       struct iwl_mvm_tcm_mac *mdata;
+
+       if (mac >= NUM_MAC_INDEX_DRIVER)
+               return;
+
+       mdata = &mvm->tcm.data[mac];
 
        if (mvm->tcm.paused)
                return;
@@ -1065,14 +1040,21 @@ static void iwl_mvm_tx_airtime(struct iwl_mvm *mvm,
        mdata->tx.airtime += airtime;
 }
 
-static void iwl_mvm_tx_pkt_queued(struct iwl_mvm *mvm,
-                                 struct iwl_mvm_sta *mvmsta, int tid)
+static int iwl_mvm_tx_pkt_queued(struct iwl_mvm *mvm,
+                                struct iwl_mvm_sta *mvmsta, int tid)
 {
        u32 ac = tid_to_mac80211_ac[tid];
        int mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK;
-       struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[mac];
+       struct iwl_mvm_tcm_mac *mdata;
+
+       if (mac >= NUM_MAC_INDEX_DRIVER)
+               return -EINVAL;
+
+       mdata = &mvm->tcm.data[mac];
 
        mdata->tx.pkts[ac]++;
+
+       return 0;
 }
 
 /*
@@ -1088,7 +1070,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
        __le16 fc;
        u16 seq_number = 0;
        u8 tid = IWL_MAX_TID_COUNT;
-       u16 txq_id = info->hw_queue;
+       u16 txq_id;
        bool is_ampdu = false;
        int hdrlen;
 
@@ -1152,14 +1134,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
 
        WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
 
-       /* Check if TXQ needs to be allocated or re-activated */
-       if (unlikely(txq_id == IWL_MVM_INVALID_QUEUE)) {
-               iwl_mvm_tx_add_stream(mvm, mvmsta, tid, skb);
-
-               /*
-                * The frame is now deferred, and the worker scheduled
-                * will re-allocate it, so we can free it for now.
-                */
+       if (WARN_ON_ONCE(txq_id == IWL_MVM_INVALID_QUEUE)) {
                iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
                spin_unlock(&mvmsta->lock);
                return 0;
@@ -1199,7 +1174,9 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
 
        spin_unlock(&mvmsta->lock);
 
-       iwl_mvm_tx_pkt_queued(mvm, mvmsta, tid == IWL_MAX_TID_COUNT ? 0 : tid);
+       if (iwl_mvm_tx_pkt_queued(mvm, mvmsta,
+                                 tid == IWL_MAX_TID_COUNT ? 0 : tid))
+               goto drop;
 
        return 0;
 
index d116c6a..211c463 100644 (file)
@@ -248,7 +248,7 @@ void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
        IWL_ERR(mvm, "FW Error notification: seq 0x%04X service 0x%08X\n",
                le16_to_cpu(err_resp->bad_cmd_seq_num),
                le32_to_cpu(err_resp->error_service));
-       IWL_ERR(mvm, "FW Error notification: timestamp 0x%16llX\n",
+       IWL_ERR(mvm, "FW Error notification: timestamp 0x%016llX\n",
                le64_to_cpu(err_resp->timestamp));
 }
 
@@ -463,6 +463,9 @@ static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm)
        iwl_trans_read_mem_bytes(trans, mvm->umac_error_event_table, &table,
                                 sizeof(table));
 
+       if (table.valid)
+               mvm->fwrt.dump.umac_err_id = table.error_id;
+
        if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
                IWL_ERR(trans, "Start IWL Error Log Dump:\n");
                IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
@@ -486,11 +489,11 @@ static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm)
        IWL_ERR(mvm, "0x%08X | isr status reg\n", table.nic_isr_pref);
 }
 
-static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u32 base)
+static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u8 lmac_num)
 {
        struct iwl_trans *trans = mvm->trans;
        struct iwl_error_event_table table;
-       u32 val;
+       u32 val, base = mvm->error_event_table[lmac_num];
 
        if (mvm->fwrt.cur_fw_img == IWL_UCODE_INIT) {
                if (!base)
@@ -541,7 +544,7 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u32 base)
        iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
 
        if (table.valid)
-               mvm->fwrt.dump.rt_status = table.error_id;
+               mvm->fwrt.dump.lmac_err_id[lmac_num] = table.error_id;
 
        if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
                IWL_ERR(trans, "Start IWL Error Log Dump:\n");
@@ -598,10 +601,10 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
                return;
        }
 
-       iwl_mvm_dump_lmac_error_log(mvm, mvm->error_event_table[0]);
+       iwl_mvm_dump_lmac_error_log(mvm, 0);
 
        if (mvm->error_event_table[1])
-               iwl_mvm_dump_lmac_error_log(mvm, mvm->error_event_table[1]);
+               iwl_mvm_dump_lmac_error_log(mvm, 1);
 
        iwl_mvm_dump_umac_error_log(mvm);
 }
@@ -1133,19 +1136,14 @@ static void iwl_mvm_tcm_uapsd_nonagg_detected_wk(struct work_struct *wk)
                                "AP isn't using AMPDU with uAPSD enabled");
 }
 
-static void iwl_mvm_uapsd_agg_disconnect_iter(void *data, u8 *mac,
-                                             struct ieee80211_vif *vif)
+static void iwl_mvm_uapsd_agg_disconnect(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       int *mac_id = data;
 
        if (vif->type != NL80211_IFTYPE_STATION)
                return;
 
-       if (mvmvif->id != *mac_id)
-               return;
-
        if (!vif->bss_conf.assoc)
                return;
 
@@ -1155,10 +1153,10 @@ static void iwl_mvm_uapsd_agg_disconnect_iter(void *data, u8 *mac,
            !mvmvif->queue_params[IEEE80211_AC_BK].uapsd)
                return;
 
-       if (mvm->tcm.data[*mac_id].uapsd_nonagg_detect.detected)
+       if (mvm->tcm.data[mvmvif->id].uapsd_nonagg_detect.detected)
                return;
 
-       mvm->tcm.data[*mac_id].uapsd_nonagg_detect.detected = true;
+       mvm->tcm.data[mvmvif->id].uapsd_nonagg_detect.detected = true;
        IWL_INFO(mvm,
                 "detected AP should do aggregation but isn't, likely due to U-APSD\n");
        schedule_delayed_work(&mvmvif->uapsd_nonagg_detected_wk, 15 * HZ);
@@ -1171,6 +1169,7 @@ static void iwl_mvm_check_uapsd_agg_expected_tpt(struct iwl_mvm *mvm,
        u64 bytes = mvm->tcm.data[mac].uapsd_nonagg_detect.rx_bytes;
        u64 tpt;
        unsigned long rate;
+       struct ieee80211_vif *vif;
 
        rate = ewma_rate_read(&mvm->tcm.data[mac].uapsd_nonagg_detect.rate);
 
@@ -1199,9 +1198,11 @@ static void iwl_mvm_check_uapsd_agg_expected_tpt(struct iwl_mvm *mvm,
                        return;
        }
 
-       ieee80211_iterate_active_interfaces_atomic(
-               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
-               iwl_mvm_uapsd_agg_disconnect_iter, &mac);
+       rcu_read_lock();
+       vif = rcu_dereference(mvm->vif_id_to_mac[mac]);
+       if (vif)
+               iwl_mvm_uapsd_agg_disconnect(mvm, vif);
+       rcu_read_unlock();
 }
 
 static void iwl_mvm_tcm_iterator(void *_data, u8 *mac,
index 353581c..a22e476 100644 (file)
@@ -513,10 +513,10 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x24FD, 0x9074, iwl8265_2ac_cfg)},
 
 /* 9000 Series */
-       {IWL_PCI_DEVICE(0x02F0, 0x0030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0030, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x02F0, 0x0034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x02F0, 0x0038, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x02F0, 0x003C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0038, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x003C, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x02F0, 0x0060, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x02F0, 0x0064, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x02F0, 0x00A0, iwl9462_2ac_cfg_soc)},
@@ -531,17 +531,17 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x02F0, 0x02A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x02F0, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x02F0, 0x1552, iwl9560_killer_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x02F0, 0x2030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x02F0, 0x2034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x02F0, 0x4030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x02F0, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x2030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x2034, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x4030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x4034, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x02F0, 0x40A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x02F0, 0x4234, iwl9560_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x02F0, 0x42A4, iwl9462_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x06F0, 0x0030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0030, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x06F0, 0x0034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x06F0, 0x0038, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x06F0, 0x003C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0038, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x003C, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x06F0, 0x0060, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x06F0, 0x0064, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x06F0, 0x00A0, iwl9462_2ac_cfg_soc)},
@@ -556,20 +556,21 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x06F0, 0x02A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x06F0, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x06F0, 0x1552, iwl9560_killer_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x06F0, 0x2030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x06F0, 0x2034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x06F0, 0x4030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x06F0, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x2030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x2034, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x4030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x4034, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x06F0, 0x40A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x06F0, 0x4234, iwl9560_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x06F0, 0x42A4, iwl9462_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x2526, 0x0010, iwl9260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2526, 0x0014, iwl9260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2526, 0x0018, iwl9260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2526, 0x0030, iwl9560_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x0010, iwl9260_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x0014, iwl9260_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x0018, iwl9260_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x001C, iwl9260_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x0030, iwl9560_2ac_160_cfg)},
        {IWL_PCI_DEVICE(0x2526, 0x0034, iwl9560_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2526, 0x0038, iwl9560_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2526, 0x003C, iwl9560_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x0038, iwl9560_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x003C, iwl9560_2ac_160_cfg)},
        {IWL_PCI_DEVICE(0x2526, 0x0060, iwl9460_2ac_cfg)},
        {IWL_PCI_DEVICE(0x2526, 0x0064, iwl9460_2ac_cfg)},
        {IWL_PCI_DEVICE(0x2526, 0x00A0, iwl9460_2ac_cfg)},
@@ -593,24 +594,26 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x2526, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x2526, 0x1552, iwl9560_killer_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x2526, 0x1610, iwl9270_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2526, 0x2030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x2526, 0x2034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x2526, 0x4010, iwl9260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2526, 0x4030, iwl9560_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2526, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x2526, 0x2030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x2526, 0x2034, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x2526, 0x4010, iwl9260_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x401C, iwl9260_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x4030, iwl9560_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x4034, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x2526, 0x40A4, iwl9460_2ac_cfg)},
        {IWL_PCI_DEVICE(0x2526, 0x4234, iwl9560_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x2526, 0x42A4, iwl9462_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x2526, 0x8014, iwl9260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2526, 0xA014, iwl9260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x8014, iwl9260_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x8010, iwl9260_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0xA014, iwl9260_2ac_160_cfg)},
        {IWL_PCI_DEVICE(0x271B, 0x0010, iwl9160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x271B, 0x0014, iwl9160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x271B, 0x0210, iwl9160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x271B, 0x0214, iwl9260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x271C, 0x0214, iwl9260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2720, 0x0034, iwl9560_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2720, 0x0038, iwl9560_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2720, 0x003C, iwl9560_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x2720, 0x0034, iwl9560_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2720, 0x0038, iwl9560_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2720, 0x003C, iwl9560_2ac_160_cfg)},
        {IWL_PCI_DEVICE(0x2720, 0x0060, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x2720, 0x0064, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x2720, 0x00A0, iwl9462_2ac_cfg_soc)},
@@ -628,17 +631,17 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x2720, 0x1210, iwl9260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x2720, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x2720, 0x1552, iwl9560_killer_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x2720, 0x2030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x2720, 0x2034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x2720, 0x4030, iwl9560_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x2720, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x2720, 0x2030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x2720, 0x2034, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x2720, 0x4030, iwl9560_2ac_160_cfg)},
+       {IWL_PCI_DEVICE(0x2720, 0x4034, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x2720, 0x40A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x2720, 0x4234, iwl9560_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x2720, 0x42A4, iwl9462_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x30DC, 0x0030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x30DC, 0x0030, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x30DC, 0x0034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x30DC, 0x0038, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x30DC, 0x003C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x30DC, 0x0038, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x30DC, 0x003C, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x30DC, 0x0060, iwl9460_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x30DC, 0x0064, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x30DC, 0x00A0, iwl9462_2ac_cfg_soc)},
@@ -656,17 +659,17 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x30DC, 0x1210, iwl9260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x30DC, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x30DC, 0x1552, iwl9560_killer_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x30DC, 0x2030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x30DC, 0x2034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x30DC, 0x4030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x30DC, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x30DC, 0x2030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x30DC, 0x2034, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x30DC, 0x4030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x30DC, 0x4034, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x30DC, 0x40A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x30DC, 0x4234, iwl9560_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x30DC, 0x42A4, iwl9462_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x31DC, 0x0030, iwl9560_2ac_cfg_shared_clk)},
+       {IWL_PCI_DEVICE(0x31DC, 0x0030, iwl9560_2ac_160_cfg_shared_clk)},
        {IWL_PCI_DEVICE(0x31DC, 0x0034, iwl9560_2ac_cfg_shared_clk)},
-       {IWL_PCI_DEVICE(0x31DC, 0x0038, iwl9560_2ac_cfg_shared_clk)},
-       {IWL_PCI_DEVICE(0x31DC, 0x003C, iwl9560_2ac_cfg_shared_clk)},
+       {IWL_PCI_DEVICE(0x31DC, 0x0038, iwl9560_2ac_160_cfg_shared_clk)},
+       {IWL_PCI_DEVICE(0x31DC, 0x003C, iwl9560_2ac_160_cfg_shared_clk)},
        {IWL_PCI_DEVICE(0x31DC, 0x0060, iwl9460_2ac_cfg_shared_clk)},
        {IWL_PCI_DEVICE(0x31DC, 0x0064, iwl9461_2ac_cfg_shared_clk)},
        {IWL_PCI_DEVICE(0x31DC, 0x00A0, iwl9462_2ac_cfg_shared_clk)},
@@ -684,18 +687,18 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x31DC, 0x1210, iwl9260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x31DC, 0x1551, iwl9560_killer_s_2ac_cfg_shared_clk)},
        {IWL_PCI_DEVICE(0x31DC, 0x1552, iwl9560_killer_2ac_cfg_shared_clk)},
-       {IWL_PCI_DEVICE(0x31DC, 0x2030, iwl9560_2ac_cfg_shared_clk)},
-       {IWL_PCI_DEVICE(0x31DC, 0x2034, iwl9560_2ac_cfg_shared_clk)},
-       {IWL_PCI_DEVICE(0x31DC, 0x4030, iwl9560_2ac_cfg_shared_clk)},
-       {IWL_PCI_DEVICE(0x31DC, 0x4034, iwl9560_2ac_cfg_shared_clk)},
+       {IWL_PCI_DEVICE(0x31DC, 0x2030, iwl9560_2ac_160_cfg_shared_clk)},
+       {IWL_PCI_DEVICE(0x31DC, 0x2034, iwl9560_2ac_160_cfg_shared_clk)},
+       {IWL_PCI_DEVICE(0x31DC, 0x4030, iwl9560_2ac_160_cfg_shared_clk)},
+       {IWL_PCI_DEVICE(0x31DC, 0x4034, iwl9560_2ac_160_cfg_shared_clk)},
        {IWL_PCI_DEVICE(0x31DC, 0x40A4, iwl9462_2ac_cfg_shared_clk)},
        {IWL_PCI_DEVICE(0x31DC, 0x4234, iwl9560_2ac_cfg_shared_clk)},
        {IWL_PCI_DEVICE(0x31DC, 0x42A4, iwl9462_2ac_cfg_shared_clk)},
 
-       {IWL_PCI_DEVICE(0x34F0, 0x0030, iwl9560_2ac_cfg_qu_b0_jf_b0)},
+       {IWL_PCI_DEVICE(0x34F0, 0x0030, iwl9560_2ac_160_cfg_qu_b0_jf_b0)},
        {IWL_PCI_DEVICE(0x34F0, 0x0034, iwl9560_2ac_cfg_qu_b0_jf_b0)},
-       {IWL_PCI_DEVICE(0x34F0, 0x0038, iwl9560_2ac_cfg_qu_b0_jf_b0)},
-       {IWL_PCI_DEVICE(0x34F0, 0x003C, iwl9560_2ac_cfg_qu_b0_jf_b0)},
+       {IWL_PCI_DEVICE(0x34F0, 0x0038, iwl9560_2ac_160_cfg_qu_b0_jf_b0)},
+       {IWL_PCI_DEVICE(0x34F0, 0x003C, iwl9560_2ac_160_cfg_qu_b0_jf_b0)},
        {IWL_PCI_DEVICE(0x34F0, 0x0060, iwl9461_2ac_cfg_qu_b0_jf_b0)},
        {IWL_PCI_DEVICE(0x34F0, 0x0064, iwl9461_2ac_cfg_qu_b0_jf_b0)},
        {IWL_PCI_DEVICE(0x34F0, 0x00A0, iwl9462_2ac_cfg_qu_b0_jf_b0)},
@@ -710,18 +713,18 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x34F0, 0x02A4, iwl9462_2ac_cfg_qu_b0_jf_b0)},
        {IWL_PCI_DEVICE(0x34F0, 0x1551, killer1550s_2ac_cfg_qu_b0_jf_b0)},
        {IWL_PCI_DEVICE(0x34F0, 0x1552, killer1550i_2ac_cfg_qu_b0_jf_b0)},
-       {IWL_PCI_DEVICE(0x34F0, 0x2030, iwl9560_2ac_cfg_qu_b0_jf_b0)},
-       {IWL_PCI_DEVICE(0x34F0, 0x2034, iwl9560_2ac_cfg_qu_b0_jf_b0)},
-       {IWL_PCI_DEVICE(0x34F0, 0x4030, iwl9560_2ac_cfg_qu_b0_jf_b0)},
-       {IWL_PCI_DEVICE(0x34F0, 0x4034, iwl9560_2ac_cfg_qu_b0_jf_b0)},
+       {IWL_PCI_DEVICE(0x34F0, 0x2030, iwl9560_2ac_160_cfg_qu_b0_jf_b0)},
+       {IWL_PCI_DEVICE(0x34F0, 0x2034, iwl9560_2ac_160_cfg_qu_b0_jf_b0)},
+       {IWL_PCI_DEVICE(0x34F0, 0x4030, iwl9560_2ac_160_cfg_qu_b0_jf_b0)},
+       {IWL_PCI_DEVICE(0x34F0, 0x4034, iwl9560_2ac_160_cfg_qu_b0_jf_b0)},
        {IWL_PCI_DEVICE(0x34F0, 0x40A4, iwl9462_2ac_cfg_qu_b0_jf_b0)},
        {IWL_PCI_DEVICE(0x34F0, 0x4234, iwl9560_2ac_cfg_qu_b0_jf_b0)},
        {IWL_PCI_DEVICE(0x34F0, 0x42A4, iwl9462_2ac_cfg_qu_b0_jf_b0)},
 
-       {IWL_PCI_DEVICE(0x3DF0, 0x0030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x3DF0, 0x0030, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x3DF0, 0x0034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x3DF0, 0x0038, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x3DF0, 0x003C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x3DF0, 0x0038, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x3DF0, 0x003C, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x3DF0, 0x0060, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x3DF0, 0x0064, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x3DF0, 0x00A0, iwl9462_2ac_cfg_soc)},
@@ -739,17 +742,17 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x3DF0, 0x1210, iwl9260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x3DF0, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x3DF0, 0x1552, iwl9560_killer_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x3DF0, 0x2030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x3DF0, 0x2034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x3DF0, 0x4030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x3DF0, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x3DF0, 0x2030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x3DF0, 0x2034, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x3DF0, 0x4030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x3DF0, 0x4034, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x3DF0, 0x40A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x3DF0, 0x4234, iwl9560_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x3DF0, 0x42A4, iwl9462_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x43F0, 0x0030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x43F0, 0x0030, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x43F0, 0x0034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x43F0, 0x0038, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x43F0, 0x003C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x43F0, 0x0038, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x43F0, 0x003C, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x43F0, 0x0060, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x43F0, 0x0064, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x43F0, 0x00A0, iwl9462_2ac_cfg_soc)},
@@ -767,19 +770,19 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x43F0, 0x1210, iwl9260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x43F0, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x43F0, 0x1552, iwl9560_killer_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x43F0, 0x2030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x43F0, 0x2034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x43F0, 0x4030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x43F0, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x43F0, 0x2030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x43F0, 0x2034, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x43F0, 0x4030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x43F0, 0x4034, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x43F0, 0x40A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x43F0, 0x4234, iwl9560_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x43F0, 0x42A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x0000, iwl9460_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x0010, iwl9460_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x0030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x9DF0, 0x0030, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x0034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x0038, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x003C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x9DF0, 0x0038, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x9DF0, 0x003C, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x0060, iwl9460_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x0064, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x00A0, iwl9462_2ac_cfg_soc)},
@@ -805,18 +808,18 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x9DF0, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x1552, iwl9560_killer_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x2010, iwl9460_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x2030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x2034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x9DF0, 0x2030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x9DF0, 0x2034, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl9460_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x4030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x9DF0, 0x4030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x9DF0, 0x4034, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x40A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x4234, iwl9560_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x9DF0, 0x42A4, iwl9462_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x0030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x0030, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0xA0F0, 0x0034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x0038, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x003C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x0038, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x003C, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0xA0F0, 0x0060, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0xA0F0, 0x0064, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0xA0F0, 0x00A0, iwl9462_2ac_cfg_soc)},
@@ -834,17 +837,17 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0xA0F0, 0x1210, iwl9260_2ac_cfg)},
        {IWL_PCI_DEVICE(0xA0F0, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0xA0F0, 0x1552, iwl9560_killer_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x2030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x2034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x4030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x2030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x2034, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x4030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x4034, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0xA0F0, 0x40A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0xA0F0, 0x4234, iwl9560_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0xA0F0, 0x42A4, iwl9462_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA370, 0x0030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA370, 0x0030, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0xA370, 0x0034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA370, 0x0038, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA370, 0x003C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA370, 0x0038, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA370, 0x003C, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0xA370, 0x0060, iwl9460_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0xA370, 0x0064, iwl9461_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0xA370, 0x00A0, iwl9462_2ac_cfg_soc)},
@@ -862,41 +865,88 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0xA370, 0x1210, iwl9260_2ac_cfg)},
        {IWL_PCI_DEVICE(0xA370, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0xA370, 0x1552, iwl9560_killer_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA370, 0x2030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA370, 0x2034, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA370, 0x4030, iwl9560_2ac_cfg_soc)},
-       {IWL_PCI_DEVICE(0xA370, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA370, 0x2030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA370, 0x2034, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA370, 0x4030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0xA370, 0x4034, iwl9560_2ac_160_cfg_soc)},
        {IWL_PCI_DEVICE(0xA370, 0x40A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0xA370, 0x4234, iwl9560_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0xA370, 0x42A4, iwl9462_2ac_cfg_soc)},
 
 /* 22000 Series */
-       {IWL_PCI_DEVICE(0x2720, 0x0000, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0x2720, 0x0040, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0x2720, 0x0078, iwl22000_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0070, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0074, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0078, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x02F0, 0x007C, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0310, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x02F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0x02F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0x02F0, 0x4070, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0070, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0074, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0078, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x06F0, 0x007C, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0310, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x06F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0x06F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0x06F0, 0x4070, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x2720, 0x0000, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x2720, 0x0030, iwl9560_2ac_160_cfg_soc)},
+       {IWL_PCI_DEVICE(0x2720, 0x0040, iwl22560_2ax_cfg_hr)},
        {IWL_PCI_DEVICE(0x2720, 0x0070, iwl22000_2ac_cfg_hr_cdb)},
-       {IWL_PCI_DEVICE(0x2720, 0x0030, iwl22000_2ac_cfg_hr_cdb)},
-       {IWL_PCI_DEVICE(0x2720, 0x1080, iwl22000_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x2720, 0x0074, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x2720, 0x0078, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x2720, 0x007C, iwl22560_2ax_cfg_hr)},
        {IWL_PCI_DEVICE(0x2720, 0x0090, iwl22000_2ac_cfg_hr_cdb)},
        {IWL_PCI_DEVICE(0x2720, 0x0310, iwl22000_2ac_cfg_hr_cdb)},
-       {IWL_PCI_DEVICE(0x34F0, 0x0040, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0x34F0, 0x0070, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0x34F0, 0x0078, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0x34F0, 0x0310, iwl22000_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x2720, 0x0A10, iwl22000_2ac_cfg_hr_cdb)},
+       {IWL_PCI_DEVICE(0x2720, 0x1080, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x2720, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0x2720, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0x2720, 0x4070, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x34F0, 0x0040, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x34F0, 0x0070, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x34F0, 0x0074, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x34F0, 0x0078, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x34F0, 0x007C, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x34F0, 0x0310, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x34F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0x34F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0x34F0, 0x4070, iwl22560_2ax_cfg_hr)},
        {IWL_PCI_DEVICE(0x40C0, 0x0000, iwl22560_2ax_cfg_su_cdb)},
        {IWL_PCI_DEVICE(0x40C0, 0x0010, iwl22560_2ax_cfg_su_cdb)},
        {IWL_PCI_DEVICE(0x40c0, 0x0090, iwl22560_2ax_cfg_su_cdb)},
        {IWL_PCI_DEVICE(0x40C0, 0x0310, iwl22560_2ax_cfg_su_cdb)},
        {IWL_PCI_DEVICE(0x40C0, 0x0A10, iwl22560_2ax_cfg_su_cdb)},
-       {IWL_PCI_DEVICE(0x43F0, 0x0040, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0x43F0, 0x0070, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0x43F0, 0x0078, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x0000, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x0040, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x0070, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x0078, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x00B0, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0xA0F0, 0x0A10, iwl22000_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x43F0, 0x0040, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x43F0, 0x0070, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x43F0, 0x0074, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x43F0, 0x0078, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x43F0, 0x007C, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0x43F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0x43F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0x43F0, 0x4070, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x0000, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x0040, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x0070, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x0074, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x0078, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x007C, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x00B0, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x0A10, iwl22560_2ax_cfg_hr)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0)},
+       {IWL_PCI_DEVICE(0xA0F0, 0x4070, iwl22560_2ax_cfg_hr)},
+
+       {IWL_PCI_DEVICE(0x2723, 0x0080, iwl22260_2ax_cfg)},
+       {IWL_PCI_DEVICE(0x2723, 0x0084, iwl22260_2ax_cfg)},
+       {IWL_PCI_DEVICE(0x2723, 0x0088, iwl22260_2ax_cfg)},
+       {IWL_PCI_DEVICE(0x2723, 0x008C, iwl22260_2ax_cfg)},
+       {IWL_PCI_DEVICE(0x2723, 0x4080, iwl22260_2ax_cfg)},
+       {IWL_PCI_DEVICE(0x2723, 0x4088, iwl22260_2ax_cfg)},
+
+       {IWL_PCI_DEVICE(0x1a56, 0x1653, killer1650w_2ax_cfg)},
+       {IWL_PCI_DEVICE(0x1a56, 0x1654, killer1650x_2ax_cfg)},
 
 #endif /* CONFIG_IWLMVM */
 
index d6fc6ce..0d16bcc 100644 (file)
@@ -1,13 +1,15 @@
 /******************************************************************************
  *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
  * Copyright(c) 2003 - 2015 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
  * Copyright(c) 2018 Intel Corporation
  *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
  * published by the Free Software Foundation.
  * more details.
  *
  * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
+ * file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <linuxwifi@intel.com>
  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2003 - 2015 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
+ * All rights reserved.
+ *
+ * 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.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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 MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * 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 DAMAGE.
+ *
  *****************************************************************************/
 #ifndef __iwl_trans_int_pcie_h__
 #define __iwl_trans_int_pcie_h__
@@ -1029,8 +1065,6 @@ static inline int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
 int iwl_pci_fw_exit_d0i3(struct iwl_trans *trans);
 int iwl_pci_fw_enter_d0i3(struct iwl_trans *trans);
 
-void iwl_pcie_enable_rx_wake(struct iwl_trans *trans, bool enable);
-
 void iwl_pcie_rx_allocator_work(struct work_struct *data);
 
 /* common functions that are used by gen2 transport */
index 9e850c2..c260d12 100644 (file)
@@ -1,13 +1,15 @@
 /******************************************************************************
  *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
  * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
  * Copyright(c) 2018 Intel Corporation
  *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
  * published by the Free Software Foundation.
  * more details.
  *
  * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
+ * file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <linuxwifi@intel.com>
  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
+ * All rights reserved.
+ *
+ * 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.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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 MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * 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 DAMAGE.
+ *
  *****************************************************************************/
 #include <linux/sched.h>
 #include <linux/wait.h>
@@ -256,6 +292,9 @@ static void iwl_pcie_restock_bd(struct iwl_trans *trans,
 
                bd[rxq->write] = cpu_to_le64(rxb->page_dma | rxb->vid);
        }
+
+       IWL_DEBUG_RX(trans, "Assigned virtual RB ID %u to queue %d index %d\n",
+                    (u32)rxb->vid, rxq->id, rxq->write);
 }
 
 /*
@@ -860,30 +899,6 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq)
                iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE);
 }
 
-void iwl_pcie_enable_rx_wake(struct iwl_trans *trans, bool enable)
-{
-       if (trans->cfg->device_family != IWL_DEVICE_FAMILY_9000)
-               return;
-
-       if (CSR_HW_REV_STEP(trans->hw_rev) != SILICON_A_STEP)
-               return;
-
-       if (!trans->cfg->integrated)
-               return;
-
-       /*
-        * Turn on the chicken-bits that cause MAC wakeup for RX-related
-        * values.
-        * This costs some power, but needed for W/A 9000 integrated A-step
-        * bug where shadow registers are not in the retention list and their
-        * value is lost when NIC powers down
-        */
-       iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL,
-                   CSR_MAC_SHADOW_REG_CTRL_RX_WAKE);
-       iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTL2,
-                   CSR_MAC_SHADOW_REG_CTL2_RX_WAKE);
-}
-
 static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -971,8 +986,6 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans)
 
        /* Set interrupt coalescing timer to default (2048 usecs) */
        iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF);
-
-       iwl_pcie_enable_rx_wake(trans, true);
 }
 
 void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq)
@@ -1360,6 +1373,8 @@ static struct iwl_rx_mem_buffer *iwl_pcie_get_rxb(struct iwl_trans *trans,
        if (rxb->invalid)
                goto out_err;
 
+       IWL_DEBUG_RX(trans, "Got virtual RB ID %u\n", (u32)rxb->vid);
+
        if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560)
                rxb->size = le32_to_cpu(rxq->cd[i].size) & IWL_RX_CD_SIZE;
 
@@ -1411,11 +1426,12 @@ restart:
                        emergency = true;
                }
 
+               IWL_DEBUG_RX(trans, "Q %d: HW = %d, SW = %d\n", rxq->id, r, i);
+
                rxb = iwl_pcie_get_rxb(trans, rxq, i);
                if (!rxb)
                        goto out;
 
-               IWL_DEBUG_RX(trans, "Q %d: HW = %d, SW = %d\n", rxq->id, r, i);
                iwl_pcie_rx_handle_rb(trans, rxq, rxb, emergency, i);
 
                i = (i + 1) & (rxq->queue_size - 1);
index f97aea5..f742815 100644 (file)
@@ -1530,8 +1530,6 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test,
        iwl_clear_bit(trans, CSR_GP_CNTRL,
                      BIT(trans->cfg->csr->flag_init_done));
 
-       iwl_pcie_enable_rx_wake(trans, false);
-
        if (reset) {
                /*
                 * reset TX queues -- some of their registers reset during S3
@@ -1558,8 +1556,6 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
                return 0;
        }
 
-       iwl_pcie_enable_rx_wake(trans, true);
-
        iwl_set_bit(trans, CSR_GP_CNTRL,
                    BIT(trans->cfg->csr->flag_mac_access_req));
        iwl_set_bit(trans, CSR_GP_CNTRL,
@@ -1968,7 +1964,7 @@ static void iwl_trans_pcie_removal_wk(struct work_struct *wk)
        struct iwl_trans_pcie_removal *removal =
                container_of(wk, struct iwl_trans_pcie_removal, work);
        struct pci_dev *pdev = removal->pdev;
-       char *prop[] = {"EVENT=INACCESSIBLE", NULL};
+       static char *prop[] = {"EVENT=INACCESSIBLE", NULL};
 
        dev_err(&pdev->dev, "Device gone - attempting removal\n");
        kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, prop);
@@ -3118,7 +3114,7 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,
        return len;
 }
 
-static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, int *len)
+static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, u32 *len)
 {
        if (trans->num_blocks) {
                *len += sizeof(struct iwl_fw_error_dump_data) +
@@ -3173,8 +3169,7 @@ static struct iwl_trans_dump_data
        struct iwl_txq *cmdq = trans_pcie->txq[trans_pcie->cmd_queue];
        struct iwl_fw_error_dump_txcmd *txcmd;
        struct iwl_trans_dump_data *dump_data;
-       u32 len, num_rbs = 0;
-       u32 monitor_len;
+       u32 len, num_rbs = 0, monitor_len = 0;
        int i, ptr;
        bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) &&
                        !trans->cfg->mq_rx_supported &&
@@ -3191,19 +3186,8 @@ static struct iwl_trans_dump_data
                cmdq->n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE);
 
        /* FW monitor */
-       monitor_len = iwl_trans_get_fw_monitor_len(trans, &len);
-
-       if (dump_mask == BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)) {
-               dump_data = vzalloc(len);
-               if (!dump_data)
-                       return NULL;
-
-               data = (void *)dump_data->data;
-               len = iwl_trans_pcie_dump_monitor(trans, &data, monitor_len);
-               dump_data->len = len;
-
-               return dump_data;
-       }
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FW_MONITOR))
+               monitor_len = iwl_trans_get_fw_monitor_len(trans, &len);
 
        /* CSR registers */
        if (dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR))
@@ -3569,24 +3553,15 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
                }
        }
 
-       /*
-        * 9000-series integrated A-step has a problem with suspend/resume
-        * and sometimes even causes the whole platform to get stuck. This
-        * workaround makes the hardware not go into the problematic state.
-        */
-       if (trans->cfg->integrated &&
-           trans->cfg->device_family == IWL_DEVICE_FAMILY_9000 &&
-           CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP)
-               iwl_set_bit(trans, CSR_HOST_CHICKEN,
-                           CSR_HOST_CHICKEN_PM_IDLE_SRC_DIS_SB_PME);
+       IWL_DEBUG_INFO(trans, "HW REV: 0x%0x\n", trans->hw_rev);
 
 #if IS_ENABLED(CONFIG_IWLMVM)
        trans->hw_rf_id = iwl_read32(trans, CSR_HW_RF_ID);
 
-       if (cfg == &iwl22000_2ax_cfg_hr) {
+       if (cfg == &iwl22560_2ax_cfg_hr) {
                if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
                    CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR)) {
-                       trans->cfg = &iwl22000_2ax_cfg_hr;
+                       trans->cfg = &iwl22560_2ax_cfg_hr;
                } else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
                           CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_JF)) {
                        trans->cfg = &iwl22000_2ax_cfg_jf;
@@ -3602,7 +3577,9 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
                        goto out_no_pci;
                }
        } else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
-                  CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR)) {
+                  CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR) &&
+                  (trans->cfg != &iwl22260_2ax_cfg ||
+                   trans->hw_rev == CSR_HW_REV_TYPE_QNJ_B0)) {
                u32 hw_status;
 
                hw_status = iwl_read_prph(trans, UMAG_GEN_HW_STATUS);
index 156ca1b..f3d2e8f 100644 (file)
@@ -214,7 +214,11 @@ static int iwl_pcie_gen2_set_tb(struct iwl_trans *trans,
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int idx = iwl_pcie_gen2_get_num_tbs(trans, tfd);
-       struct iwl_tfh_tb *tb = &tfd->tbs[idx];
+       struct iwl_tfh_tb *tb;
+
+       if (WARN_ON(idx >= IWL_TFH_NUM_TBS))
+               return -EINVAL;
+       tb = &tfd->tbs[idx];
 
        /* Each TFD can point to a maximum max_tbs Tx buffers */
        if (le16_to_cpu(tfd->num_tbs) >= trans_pcie->max_tbs) {
@@ -408,7 +412,7 @@ iwl_tfh_tfd *iwl_pcie_gen2_build_tx_amsdu(struct iwl_trans *trans,
                goto out_err;
 
        /* building the A-MSDU might have changed this data, memcpy it now */
-       memcpy(&txq->first_tb_bufs[idx], &dev_cmd->hdr, IWL_FIRST_TB_SIZE);
+       memcpy(&txq->first_tb_bufs[idx], dev_cmd, IWL_FIRST_TB_SIZE);
        return tfd;
 
 out_err:
@@ -469,7 +473,7 @@ iwl_tfh_tfd *iwl_pcie_gen2_build_tx(struct iwl_trans *trans,
        tb_phys = iwl_pcie_get_first_tb_dma(txq, idx);
 
        /* The first TB points to bi-directional DMA data */
-       memcpy(&txq->first_tb_bufs[idx], &dev_cmd->hdr, IWL_FIRST_TB_SIZE);
+       memcpy(&txq->first_tb_bufs[idx], dev_cmd, IWL_FIRST_TB_SIZE);
 
        iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, IWL_FIRST_TB_SIZE);
 
@@ -834,14 +838,14 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
 
        /* start the TFD with the minimum copy bytes */
        tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE);
-       memcpy(&txq->first_tb_bufs[idx], &out_cmd->hdr, tb0_size);
+       memcpy(&txq->first_tb_bufs[idx], out_cmd, tb0_size);
        iwl_pcie_gen2_set_tb(trans, tfd, iwl_pcie_get_first_tb_dma(txq, idx),
                             tb0_size);
 
        /* map first command fragment, if any remains */
        if (copy_size > tb0_size) {
                phys_addr = dma_map_single(trans->dev,
-                                          ((u8 *)&out_cmd->hdr) + tb0_size,
+                                          (u8 *)out_cmd + tb0_size,
                                           copy_size - tb0_size,
                                           DMA_TO_DEVICE);
                if (dma_mapping_error(trans->dev, phys_addr)) {
index ee990a7..0739550 100644 (file)
@@ -1,13 +1,15 @@
 /******************************************************************************
  *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
  * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
  * Copyright(c) 2018 Intel Corporation
  *
- * Portions of this file are derived from the ipw3945 project, as well
- * as portions of the ieee80211 subsystem header files.
- *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
  * published by the Free Software Foundation.
  * more details.
  *
  * The full GNU General Public License is included in this distribution in the
- * file called LICENSE.
+ * file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <linuxwifi@intel.com>
  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
+ * All rights reserved.
+ *
+ * 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.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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 MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * 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 DAMAGE.
+ *
  *****************************************************************************/
 #include <linux/etherdevice.h>
 #include <linux/ieee80211.h>
@@ -2438,8 +2474,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        }
 
        /* building the A-MSDU might have changed this data, so memcpy it now */
-       memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr,
-              IWL_FIRST_TB_SIZE);
+       memcpy(&txq->first_tb_bufs[txq->write_ptr], dev_cmd, IWL_FIRST_TB_SIZE);
 
        tfd = iwl_pcie_get_tfd(trans, txq, txq->write_ptr);
        /* Set up entry for this TFD in Tx byte-count array */
index c83f44f..fe14814 100644 (file)
@@ -708,8 +708,6 @@ void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev)
                goto exit;
 
        priv->debugfs_dir = debugfs_create_dir(dev->name, lbs_dir);
-       if (!priv->debugfs_dir)
-               goto exit;
 
        for (i=0; i<ARRAY_SIZE(debugfs_files); i++) {
                files = &debugfs_files[i];
@@ -721,8 +719,6 @@ void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev)
        }
 
        priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir);
-       if (!priv->events_dir)
-               goto exit;
 
        for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) {
                files = &debugfs_events_files[i];
@@ -734,8 +730,6 @@ void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev)
        }
 
        priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir);
-       if (!priv->regs_dir)
-               goto exit;
 
        for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) {
                files = &debugfs_regs_files[i];
index b0cb16e..2315fdf 100644 (file)
@@ -797,7 +797,12 @@ static void lbs_persist_config_init(struct net_device *dev)
 {
        int ret;
        ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group);
+       if (ret)
+               pr_err("failed to create boot_opts_group.\n");
+
        ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group);
+       if (ret)
+               pr_err("failed to create mesh_ie_group.\n");
 }
 
 static void lbs_persist_config_remove(struct net_device *dev)
index 1d45da1..a7cb7d0 100644 (file)
@@ -676,7 +676,7 @@ int lbtf_remove_card(struct lbtf_private *priv)
        ieee80211_unregister_hw(hw);
        ieee80211_free_hw(hw);
 
-    lbtf_deb_leave(LBTF_DEB_MAIN);
+       lbtf_deb_leave(LBTF_DEB_MAIN);
        return 0;
 }
 EXPORT_SYMBOL_GPL(lbtf_remove_card);
index 279167d..524fd56 100644 (file)
@@ -9,7 +9,7 @@ config MWIFIEX
          mwifiex.
 
 config MWIFIEX_SDIO
-       tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897/SD8997"
+       tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897/SD8977/SD8997"
        depends on MWIFIEX && MMC
        select FW_LOADER
        select WANT_DEV_COREDUMP
index cbe4493..8ab114c 100644 (file)
@@ -922,9 +922,8 @@ mwifiex_reset_write(struct file *file,
 }
 
 #define MWIFIEX_DFS_ADD_FILE(name) do {                                 \
-       if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir,        \
-                       priv, &mwifiex_dfs_##name##_fops))              \
-               return;                                                 \
+       debugfs_create_file(#name, 0644, priv->dfs_dev_dir, priv,       \
+                           &mwifiex_dfs_##name##_fops);                \
 } while (0);
 
 #define MWIFIEX_DFS_FILE_OPS(name)                                      \
index d49fbd5..a856483 100644 (file)
@@ -489,6 +489,8 @@ static void mwifiex_sdio_coredump(struct device *dev)
 #define SDIO_DEVICE_ID_MARVELL_8887   (0x9135)
 /* Device ID for SD8801 */
 #define SDIO_DEVICE_ID_MARVELL_8801   (0x9139)
+/* Device ID for SD8977 */
+#define SDIO_DEVICE_ID_MARVELL_8977   (0x9145)
 /* Device ID for SD8997 */
 #define SDIO_DEVICE_ID_MARVELL_8997   (0x9141)
 
@@ -507,6 +509,8 @@ static const struct sdio_device_id mwifiex_ids[] = {
                .driver_data = (unsigned long)&mwifiex_sdio_sd8887},
        {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801),
                .driver_data = (unsigned long)&mwifiex_sdio_sd8801},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977),
+               .driver_data = (unsigned long)&mwifiex_sdio_sd8977},
        {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997),
                .driver_data = (unsigned long)&mwifiex_sdio_sd8997},
        {},
@@ -2726,4 +2730,5 @@ MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(SD8977_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME);
index dccf7fd..912de2c 100644 (file)
@@ -36,6 +36,7 @@
 #define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin"
 #define SD8887_DEFAULT_FW_NAME "mrvl/sd8887_uapsta.bin"
 #define SD8801_DEFAULT_FW_NAME "mrvl/sd8801_uapsta.bin"
+#define SD8977_DEFAULT_FW_NAME "mrvl/sd8977_uapsta.bin"
 #define SD8997_DEFAULT_FW_NAME "mrvl/sd8997_uapsta.bin"
 
 #define BLOCK_MODE     1
@@ -371,6 +372,59 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = {
                                 0x59, 0x5c, 0x5d},
 };
 
+static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8977 = {
+       .start_rd_port = 0,
+       .start_wr_port = 0,
+       .base_0_reg = 0xF8,
+       .base_1_reg = 0xF9,
+       .poll_reg = 0x5C,
+       .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK |
+               CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK,
+       .host_int_rsr_reg = 0x4,
+       .host_int_status_reg = 0x0C,
+       .host_int_mask_reg = 0x08,
+       .status_reg_0 = 0xE8,
+       .status_reg_1 = 0xE9,
+       .sdio_int_mask = 0xff,
+       .data_port_mask = 0xffffffff,
+       .io_port_0_reg = 0xE4,
+       .io_port_1_reg = 0xE5,
+       .io_port_2_reg = 0xE6,
+       .max_mp_regs = 196,
+       .rd_bitmap_l = 0x10,
+       .rd_bitmap_u = 0x11,
+       .rd_bitmap_1l = 0x12,
+       .rd_bitmap_1u = 0x13,
+       .wr_bitmap_l = 0x14,
+       .wr_bitmap_u = 0x15,
+       .wr_bitmap_1l = 0x16,
+       .wr_bitmap_1u = 0x17,
+       .rd_len_p0_l = 0x18,
+       .rd_len_p0_u = 0x19,
+       .card_misc_cfg_reg = 0xd8,
+       .card_cfg_2_1_reg = 0xd9,
+       .cmd_rd_len_0 = 0xc0,
+       .cmd_rd_len_1 = 0xc1,
+       .cmd_rd_len_2 = 0xc2,
+       .cmd_rd_len_3 = 0xc3,
+       .cmd_cfg_0 = 0xc4,
+       .cmd_cfg_1 = 0xc5,
+       .cmd_cfg_2 = 0xc6,
+       .cmd_cfg_3 = 0xc7,
+       .fw_dump_host_ready = 0xcc,
+       .fw_dump_ctrl = 0xf0,
+       .fw_dump_start = 0xf1,
+       .fw_dump_end = 0xf8,
+       .func1_dump_reg_start = 0x10,
+       .func1_dump_reg_end = 0x17,
+       .func1_scratch_reg = 0xe8,
+       .func1_spec_reg_num = 13,
+       .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D,
+                                0x60, 0x61, 0x62, 0x64,
+                                0x65, 0x66, 0x68, 0x69,
+                                0x6a},
+};
+
 static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8997 = {
        .start_rd_port = 0,
        .start_wr_port = 0,
@@ -532,6 +586,22 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
        .can_ext_scan = true,
 };
 
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8977 = {
+       .firmware = SD8977_DEFAULT_FW_NAME,
+       .reg = &mwifiex_reg_sd8977,
+       .max_ports = 32,
+       .mp_agg_pkt_limit = 16,
+       .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
+       .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
+       .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
+       .supports_sdio_new_mode = true,
+       .has_control_mask = false,
+       .can_dump_fw = true,
+       .fw_dump_enh = true,
+       .can_auto_tdls = false,
+       .can_ext_scan = true,
+};
+
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = {
        .firmware = SD8997_DEFAULT_FW_NAME,
        .reg = &mwifiex_reg_sd8997,
index e2ba263..e769c8a 100644 (file)
@@ -300,7 +300,7 @@ int mt76_dma_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
        if (q->queued + (n + 1) / 2 >= q->ndesc - 1)
                goto unmap;
 
-       return dev->queue_ops->add_buf(dev, q, buf, n, tx_info, skb, t);
+       return mt76_dma_add_buf(dev, q, buf, n, tx_info, skb, t);
 
 unmap:
        ret = -ENOMEM;
@@ -318,7 +318,7 @@ free:
 EXPORT_SYMBOL_GPL(mt76_dma_tx_queue_skb);
 
 static int
-mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q, bool napi)
+mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
 {
        dma_addr_t addr;
        void *buf;
@@ -392,7 +392,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
 
        mt76_dma_rx_cleanup(dev, q);
        mt76_dma_sync_idx(dev, q);
-       mt76_dma_rx_fill(dev, q, false);
+       mt76_dma_rx_fill(dev, q);
 }
 
 static void
@@ -417,10 +417,9 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
 static int
 mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
 {
+       int len, data_len, done = 0;
        struct sk_buff *skb;
        unsigned char *data;
-       int len;
-       int done = 0;
        bool more;
 
        while (done < budget) {
@@ -430,6 +429,19 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
                if (!data)
                        break;
 
+               if (q->rx_head)
+                       data_len = q->buf_size;
+               else
+                       data_len = SKB_WITH_OVERHEAD(q->buf_size);
+
+               if (data_len < len + q->buf_offset) {
+                       dev_kfree_skb(q->rx_head);
+                       q->rx_head = NULL;
+
+                       skb_free_frag(data);
+                       continue;
+               }
+
                if (q->rx_head) {
                        mt76_add_fragment(dev, q, data, len, more);
                        continue;
@@ -440,12 +452,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
                        skb_free_frag(data);
                        continue;
                }
-
                skb_reserve(skb, q->buf_offset);
-               if (skb->tail + len > skb->end) {
-                       dev_kfree_skb(skb);
-                       continue;
-               }
 
                if (q == &dev->q_rx[MT_RXQ_MCU]) {
                        u32 *rxfce = (u32 *) skb->cb;
@@ -463,7 +470,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
                dev->drv->rx_skb(dev, q - dev->q_rx, skb);
        }
 
-       mt76_dma_rx_fill(dev, q, true);
+       mt76_dma_rx_fill(dev, q);
        return done;
 }
 
@@ -504,7 +511,7 @@ mt76_dma_init(struct mt76_dev *dev)
        for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) {
                netif_napi_add(&dev->napi_dev, &dev->napi[i], mt76_dma_rx_poll,
                               64);
-               mt76_dma_rx_fill(dev, &dev->q_rx[i], false);
+               mt76_dma_rx_fill(dev, &dev->q_rx[i]);
                skb_queue_head_init(&dev->rx_skb[i]);
                napi_enable(&dev->napi[i]);
        }
index 7b926df..ee3b65a 100644 (file)
@@ -328,6 +328,7 @@ int mt76_register_device(struct mt76_dev *dev, bool vht,
        ieee80211_hw_set(hw, MFP_CAPABLE);
        ieee80211_hw_set(hw, AP_LINK_PS);
        ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
+       ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
 
        wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
 
@@ -547,7 +548,7 @@ mt76_check_ccmp_pn(struct sk_buff *skb)
 }
 
 static void
-mt76_check_ps(struct mt76_dev *dev, struct sk_buff *skb)
+mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
 {
        struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -566,6 +567,11 @@ mt76_check_ps(struct mt76_dev *dev, struct sk_buff *skb)
 
        sta = container_of((void *) wcid, struct ieee80211_sta, drv_priv);
 
+       if (status->signal <= 0)
+               ewma_signal_add(&wcid->rssi, -status->signal);
+
+       wcid->inactive_count = 0;
+
        if (!test_bit(MT_WCID_FLAG_CHECK_PS, &wcid->flags))
                return;
 
@@ -625,7 +631,7 @@ void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q,
        __skb_queue_head_init(&frames);
 
        while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) {
-               mt76_check_ps(dev, skb);
+               mt76_check_sta(dev, skb);
                mt76_rx_aggr_reorder(skb, &frames);
        }
 
@@ -659,6 +665,7 @@ mt76_sta_add(struct mt76_dev *dev, struct ieee80211_vif *vif,
                mt76_txq_init(dev, sta->txq[i]);
        }
 
+       ewma_signal_init(&wcid->rssi);
        rcu_assign_pointer(dev->wcid[wcid->idx], wcid);
 
 out:
@@ -709,3 +716,60 @@ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        return 0;
 }
 EXPORT_SYMBOL_GPL(mt76_sta_state);
+
+int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                    int *dbm)
+{
+       struct mt76_dev *dev = hw->priv;
+       int n_chains = __sw_hweight8(dev->antenna_mask);
+
+       *dbm = dev->txpower_cur / 2;
+
+       /* convert from per-chain power to combined
+        * output on 2x2 devices
+        */
+       if (n_chains > 1)
+               *dbm += 3;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mt76_get_txpower);
+
+static void
+__mt76_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+       if (vif->csa_active && ieee80211_csa_is_complete(vif))
+           ieee80211_csa_finish(vif);
+}
+
+void mt76_csa_finish(struct mt76_dev *dev)
+{
+       if (!dev->csa_complete)
+               return;
+
+       ieee80211_iterate_active_interfaces_atomic(dev->hw,
+               IEEE80211_IFACE_ITER_RESUME_ALL,
+               __mt76_csa_finish, dev);
+
+       dev->csa_complete = 0;
+}
+EXPORT_SYMBOL_GPL(mt76_csa_finish);
+
+static void
+__mt76_csa_check(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct mt76_dev *dev = priv;
+
+       if (!vif->csa_active)
+               return;
+
+       dev->csa_complete |= ieee80211_csa_is_complete(vif);
+}
+
+void mt76_csa_check(struct mt76_dev *dev)
+{
+       ieee80211_iterate_active_interfaces_atomic(dev->hw,
+               IEEE80211_IFACE_ITER_RESUME_ALL,
+               __mt76_csa_check, dev);
+}
+EXPORT_SYMBOL_GPL(mt76_csa_check);
index 5cd508a..2bb9db4 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/skbuff.h>
 #include <linux/leds.h>
 #include <linux/usb.h>
+#include <linux/average.h>
 #include <net/mac80211.h>
 #include "util.h"
 
@@ -174,6 +175,8 @@ enum mt76_wcid_flags {
 
 #define MT76_N_WCIDS 128
 
+DECLARE_EWMA(signal, 10, 8);
+
 struct mt76_wcid {
        struct mt76_rx_tid __rcu *aggr[IEEE80211_NUM_TIDS];
 
@@ -181,6 +184,9 @@ struct mt76_wcid {
 
        unsigned long flags;
 
+       struct ewma_signal rssi;
+       int inactive_count;
+
        u8 idx;
        u8 hw_key_idx;
 
@@ -239,7 +245,9 @@ struct mt76_rx_tid {
 #define MT_TX_CB_TXS_FAILED            BIT(2)
 
 #define MT_PACKET_ID_MASK              GENMASK(7, 0)
-#define MT_PACKET_ID_NO_ACK            MT_PACKET_ID_MASK
+#define MT_PACKET_ID_NO_ACK            0
+#define MT_PACKET_ID_NO_SKB            1
+#define MT_PACKET_ID_FIRST             2
 
 #define MT_TX_STATUS_SKB_TIMEOUT       HZ
 
@@ -421,6 +429,7 @@ struct mt76_dev {
        struct mt76_queue q_tx[__MT_TXQ_MAX];
        struct mt76_queue q_rx[__MT_RXQ_MAX];
        const struct mt76_queue_ops *queue_ops;
+       int tx_dma_idx[4];
 
        wait_queue_head_t tx_wait;
        struct sk_buff_head status_list;
@@ -454,6 +463,8 @@ struct mt76_dev {
        bool led_al;
        u8 led_pin;
 
+       u8 csa_complete;
+
        u32 rxfilter;
 
        union {
@@ -488,7 +499,7 @@ struct mt76_rx_status {
        u8 rate_idx;
        u8 nss;
        u8 band;
-       u8 signal;
+       s8 signal;
        u8 chains;
        s8 chain_signal[IEEE80211_MAX_CHAINS];
 };
@@ -677,6 +688,14 @@ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 
 struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb);
 
+int mt76_get_min_avg_rssi(struct mt76_dev *dev);
+
+int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                    int *dbm);
+
+void mt76_csa_check(struct mt76_dev *dev);
+void mt76_csa_finish(struct mt76_dev *dev);
+
 /* internal */
 void mt76_tx_free(struct mt76_dev *dev);
 struct mt76_txwi_cache *mt76_get_txwi(struct mt76_dev *dev);
index 497e762..b2cabce 100644 (file)
@@ -212,24 +212,24 @@ void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev)
        mt76x02_add_rate_power_offset(t, delta);
 }
 
-void mt76x0_get_power_info(struct mt76x02_dev *dev, u8 *info)
+void mt76x0_get_power_info(struct mt76x02_dev *dev, s8 *tp)
 {
        struct mt76x0_chan_map {
                u8 chan;
                u8 offset;
        } chan_map[] = {
-               {   2,  0 }, {   4,  1 }, {   6,  2 }, {   8,  3 },
-               {  10,  4 }, {  12,  5 }, {  14,  6 }, {  38,  0 },
-               {  44,  1 }, {  48,  2 }, {  54,  3 }, {  60,  4 },
-               {  64,  5 }, { 102,  6 }, { 108,  7 }, { 112,  8 },
-               { 118,  9 }, { 124, 10 }, { 128, 11 }, { 134, 12 },
-               { 140, 13 }, { 151, 14 }, { 157, 15 }, { 161, 16 },
-               { 167, 17 }, { 171, 18 }, { 173, 19 },
+               {   2,  0 }, {   4,  2 }, {   6,  4 }, {   8,  6 },
+               {  10,  8 }, {  12, 10 }, {  14, 12 }, {  38,  0 },
+               {  44,  2 }, {  48,  4 }, {  54,  6 }, {  60,  8 },
+               {  64, 10 }, { 102, 12 }, { 108, 14 }, { 112, 16 },
+               { 118, 18 }, { 124, 20 }, { 128, 22 }, { 134, 24 },
+               { 140, 26 }, { 151, 28 }, { 157, 30 }, { 161, 32 },
+               { 167, 34 }, { 171, 36 }, { 175, 38 },
        };
        struct ieee80211_channel *chan = dev->mt76.chandef.chan;
        u8 offset, addr;
+       int i, idx = 0;
        u16 data;
-       int i;
 
        if (mt76x0_tssi_enabled(dev)) {
                s8 target_power;
@@ -239,14 +239,14 @@ void mt76x0_get_power_info(struct mt76x02_dev *dev, u8 *info)
                else
                        data = mt76x02_eeprom_get(dev, MT_EE_2G_TARGET_POWER);
                target_power = (data & 0xff) - dev->mt76.rate_power.ofdm[7];
-               info[0] = target_power + mt76x0_get_delta(dev);
-               info[1] = 0;
+               *tp = target_power + mt76x0_get_delta(dev);
 
                return;
        }
 
        for (i = 0; i < ARRAY_SIZE(chan_map); i++) {
-               if (chan_map[i].chan <= chan->hw_value) {
+               if (chan->hw_value <= chan_map[i].chan) {
+                       idx = (chan->hw_value == chan_map[i].chan);
                        offset = chan_map[i].offset;
                        break;
                }
@@ -258,13 +258,16 @@ void mt76x0_get_power_info(struct mt76x02_dev *dev, u8 *info)
                addr = MT_EE_TX_POWER_DELTA_BW80 + offset;
        } else {
                switch (chan->hw_value) {
+               case 42:
+                       offset = 2;
+                       break;
                case 58:
                        offset = 8;
                        break;
                case 106:
                        offset = 14;
                        break;
-               case 112:
+               case 122:
                        offset = 20;
                        break;
                case 155:
@@ -277,14 +280,9 @@ void mt76x0_get_power_info(struct mt76x02_dev *dev, u8 *info)
        }
 
        data = mt76x02_eeprom_get(dev, addr);
-
-       info[0] = data;
-       if (!info[0] || info[0] > 0x3f)
-               info[0] = 5;
-
-       info[1] = data >> 8;
-       if (!info[1] || info[1] > 0x3f)
-               info[1] = 5;
+       *tp = data >> (8 * idx);
+       if (*tp < 0 || *tp > 0x3f)
+               *tp = 5;
 }
 
 static int mt76x0_check_eeprom(struct mt76x02_dev *dev)
index ee9ade9..42b259f 100644 (file)
@@ -26,7 +26,7 @@ struct mt76x02_dev;
 int mt76x0_eeprom_init(struct mt76x02_dev *dev);
 void mt76x0_read_rx_gain(struct mt76x02_dev *dev);
 void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev);
-void mt76x0_get_power_info(struct mt76x02_dev *dev, u8 *info);
+void mt76x0_get_power_info(struct mt76x02_dev *dev, s8 *tp);
 
 static inline s8 s6_to_s8(u32 val)
 {
index a165792..0290ba5 100644 (file)
@@ -88,6 +88,7 @@ static const struct mt76_reg_pair mt76x0_mac_reg_table[] = {
        { MT_TX_PROT_CFG6,              0xe3f42004 },
        { MT_TX_PROT_CFG7,              0xe3f42084 },
        { MT_TX_PROT_CFG8,              0xe3f42104 },
+       { MT_VHT_HT_FBK_CFG1,           0xedcba980 },
 };
 
 static const struct mt76_reg_pair mt76x0_bbp_init_tab[] = {
index d895b6f..1472c86 100644 (file)
@@ -99,7 +99,7 @@ static const struct ieee80211_ops mt76x0e_ops = {
        .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update,
        .wake_tx_queue = mt76_wake_tx_queue,
        .get_survey = mt76_get_survey,
-       .get_txpower = mt76x02_get_txpower,
+       .get_txpower = mt76_get_txpower,
        .flush = mt76x0e_flush,
        .set_tim = mt76x0e_set_tim,
        .release_buffered_frames = mt76_release_buffered_frames,
@@ -141,6 +141,15 @@ static int mt76x0e_register_device(struct mt76x02_dev *dev)
        mt76_clear(dev, 0x110, BIT(9));
        mt76_set(dev, MT_MAX_LEN_CFG, BIT(13));
 
+       mt76_wr(dev, MT_CH_TIME_CFG,
+               MT_CH_TIME_CFG_TIMER_EN |
+               MT_CH_TIME_CFG_TX_AS_BUSY |
+               MT_CH_TIME_CFG_RX_AS_BUSY |
+               MT_CH_TIME_CFG_NAV_AS_BUSY |
+               MT_CH_TIME_CFG_EIFS_AS_BUSY |
+               MT_CH_CCA_RC_EN |
+               FIELD_PREP(MT_CH_TIME_CFG_CH_TIMER_CLR, 1));
+
        err = mt76x0_register_device(dev);
        if (err < 0)
                return err;
index 1eb1a80..1117cdc 100644 (file)
@@ -845,17 +845,17 @@ static void mt76x0_phy_tssi_calibrate(struct mt76x02_dev *dev)
 void mt76x0_phy_set_txpower(struct mt76x02_dev *dev)
 {
        struct mt76_rate_power *t = &dev->mt76.rate_power;
-       u8 info[2];
+       s8 info;
 
        mt76x0_get_tx_power_per_rate(dev);
-       mt76x0_get_power_info(dev, info);
+       mt76x0_get_power_info(dev, &info);
 
-       mt76x02_add_rate_power_offset(t, info[0]);
+       mt76x02_add_rate_power_offset(t, info);
        mt76x02_limit_rate_power(t, dev->mt76.txpower_conf);
        dev->mt76.txpower_cur = mt76x02_get_max_rate_power(t);
-       mt76x02_add_rate_power_offset(t, -info[0]);
+       mt76x02_add_rate_power_offset(t, -info);
 
-       mt76x02_phy_set_txpower(dev, info[0], info[1]);
+       mt76x02_phy_set_txpower(dev, info, info);
 }
 
 void mt76x0_phy_calibrate(struct mt76x02_dev *dev, bool power_on)
@@ -1013,6 +1013,8 @@ int mt76x0_phy_set_channel(struct mt76x02_dev *dev,
        mt76x0_phy_calibrate(dev, false);
        mt76x0_phy_set_txpower(dev);
 
+       mt76x02_edcca_init(dev);
+
        ieee80211_queue_delayed_work(dev->mt76.hw, &dev->cal_work,
                                     MT_CALIBRATE_INTERVAL);
 
@@ -1075,7 +1077,9 @@ mt76x0_phy_update_channel_gain(struct mt76x02_dev *dev)
        u8 gain_delta;
        int low_gain;
 
-       dev->cal.avg_rssi_all = mt76x02_phy_get_min_avg_rssi(dev);
+       dev->cal.avg_rssi_all = mt76_get_min_avg_rssi(&dev->mt76);
+       if (!dev->cal.avg_rssi_all)
+               dev->cal.avg_rssi_all = -75;
 
        low_gain = (dev->cal.avg_rssi_all > mt76x02_get_rssi_gain_thresh(dev)) +
                   (dev->cal.avg_rssi_all > mt76x02_get_low_rssi_gain_thresh(dev));
index 0e6b43b..f66e1b2 100644 (file)
@@ -155,7 +155,7 @@ static const struct ieee80211_ops mt76x0u_ops = {
        .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update,
        .set_rts_threshold = mt76x02_set_rts_threshold,
        .wake_tx_queue = mt76_wake_tx_queue,
-       .get_txpower = mt76x02_get_txpower,
+       .get_txpower = mt76_get_txpower,
 };
 
 static int mt76x0u_register_device(struct mt76x02_dev *dev)
index 9d75850..f391d2d 100644 (file)
@@ -15,6 +15,7 @@
  */
 #include <linux/kernel.h>
 #include <linux/firmware.h>
+#include <linux/module.h>
 
 #include "mt76x0.h"
 #include "mcu.h"
index 6782665..6d96766 100644 (file)
@@ -15,8 +15,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#ifndef __MT76X02_UTIL_H
-#define __MT76X02_UTIL_H
+#ifndef __MT76x02_H
+#define __MT76x02_H
 
 #include <linux/kfifo.h>
 
@@ -28,6 +28,9 @@
 
 #define MT_CALIBRATE_INTERVAL  HZ
 
+#define MT_WATCHDOG_TIME       (HZ / 10)
+#define MT_TX_HANG_TH          10
+
 #define MT_MAX_CHAINS          2
 struct mt76x02_rx_freq_cal {
        s8 high_gain[MT_MAX_CHAINS];
@@ -79,6 +82,7 @@ struct mt76x02_dev {
        struct tasklet_struct pre_tbtt_tasklet;
        struct delayed_work cal_work;
        struct delayed_work mac_work;
+       struct delayed_work wdt_work;
 
        u32 aggr_stats[32];
 
@@ -89,6 +93,9 @@ struct mt76x02_dev {
        u8 tbtt_count;
        u16 beacon_int;
 
+       u32 tx_hang_reset;
+       u8 tx_hang_check;
+
        struct mt76x02_calibration cal;
 
        s8 target_power;
@@ -101,6 +108,12 @@ struct mt76x02_dev {
        u8 slottime;
 
        struct mt76x02_dfs_pattern_detector dfs_pd;
+
+       /* edcca monitor */
+       bool ed_tx_blocked;
+       bool ed_monitor;
+       u8 ed_trigger;
+       u8 ed_silent;
 };
 
 extern struct ieee80211_rate mt76x02_rates[12];
@@ -136,6 +149,7 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev,
                                const struct ieee80211_tx_rate *rate);
 s8 mt76x02_tx_get_txpwr_adj(struct mt76x02_dev *dev, s8 txpwr,
                            s8 max_txpwr_adj);
+void mt76x02_wdt_work(struct work_struct *work);
 void mt76x02_tx_set_txpwr_auto(struct mt76x02_dev *dev, s8 txpwr);
 void mt76x02_set_tx_ackto(struct mt76x02_dev *dev);
 void mt76x02_set_coverage_class(struct ieee80211_hw *hw,
@@ -158,8 +172,6 @@ void mt76x02_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                     const u8 *mac);
 void mt76x02_sw_scan_complete(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif);
-int mt76x02_get_txpower(struct ieee80211_hw *hw,
-                       struct ieee80211_vif *vif, int *dbm);
 void mt76x02_sta_ps(struct mt76_dev *dev, struct ieee80211_sta *sta, bool ps);
 void mt76x02_bss_info_changed(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
@@ -224,4 +236,4 @@ mt76x02_rx_get_sta_wcid(struct mt76x02_sta *sta, bool unicast)
                return &sta->vif->group_wcid;
 }
 
-#endif
+#endif /* __MT76x02_H */
index a9d52ba..7580c5c 100644 (file)
@@ -133,5 +133,7 @@ void mt76x02_init_debugfs(struct mt76x02_dev *dev)
                                    read_txpower);
 
        debugfs_create_devm_seqfile(dev->mt76.dev, "agc", dir, read_agc);
+
+       debugfs_create_u32("tx_hang_reset", 0400, dir, &dev->tx_hang_reset);
 }
 EXPORT_SYMBOL_GPL(mt76x02_init_debugfs);
index 054609c..19fdcab 100644 (file)
@@ -881,12 +881,18 @@ mt76x02_dfs_set_domain(struct mt76x02_dev *dev,
 {
        struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
 
+       mutex_lock(&dev->mt76.mutex);
        if (dfs_pd->region != region) {
                tasklet_disable(&dfs_pd->dfs_tasklet);
+
+               dev->ed_monitor = region == NL80211_DFS_ETSI;
+               mt76x02_edcca_init(dev);
+
                dfs_pd->region = region;
                mt76x02_dfs_init_params(dev);
                tasklet_enable(&dfs_pd->dfs_tasklet);
        }
+       mutex_unlock(&dev->mt76.mutex);
 }
 
 void mt76x02_regd_notifier(struct wiphy *wiphy,
index c08bf37..63fa27d 100644 (file)
@@ -130,10 +130,8 @@ static __le16
 mt76x02_mac_tx_rate_val(struct mt76x02_dev *dev,
                        const struct ieee80211_tx_rate *rate, u8 *nss_val)
 {
+       u8 phy, rate_idx, nss, bw = 0;
        u16 rateval;
-       u8 phy, rate_idx;
-       u8 nss = 1;
-       u8 bw = 0;
 
        if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
                rate_idx = rate->idx;
@@ -164,7 +162,7 @@ mt76x02_mac_tx_rate_val(struct mt76x02_dev *dev,
 
                phy = val >> 8;
                rate_idx = val & 0xff;
-               bw = 0;
+               nss = 1;
        }
 
        rateval = FIELD_PREP(MT_RXWI_RATE_INDEX, rate_idx);
@@ -435,7 +433,7 @@ void mt76x02_send_tx_status(struct mt76x02_dev *dev,
        }
 
        if (wcid) {
-               if (stat->pktid)
+               if (stat->pktid >= MT_PACKET_ID_FIRST)
                        status.skb = mt76_tx_status_skb_get(mdev, wcid,
                                                            stat->pktid, &list);
                if (status.skb)
@@ -478,7 +476,9 @@ out:
 }
 
 static int
-mt76x02_mac_process_rate(struct mt76_rx_status *status, u16 rate)
+mt76x02_mac_process_rate(struct mt76x02_dev *dev,
+                        struct mt76_rx_status *status,
+                        u16 rate)
 {
        u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate);
 
@@ -510,11 +510,15 @@ mt76x02_mac_process_rate(struct mt76_rx_status *status, u16 rate)
                status->encoding = RX_ENC_HT;
                status->rate_idx = idx;
                break;
-       case MT_PHY_TYPE_VHT:
+       case MT_PHY_TYPE_VHT: {
+               u8 n_rxstream = dev->mt76.chainmask & 0xf;
+
                status->encoding = RX_ENC_VHT;
                status->rate_idx = FIELD_GET(MT_RATE_INDEX_VHT_IDX, idx);
-               status->nss = FIELD_GET(MT_RATE_INDEX_VHT_NSS, idx) + 1;
+               status->nss = min_t(u8, n_rxstream,
+                                   FIELD_GET(MT_RATE_INDEX_VHT_NSS, idx) + 1);
                break;
+       }
        default:
                return -EINVAL;
        }
@@ -644,7 +648,7 @@ int mt76x02_mac_process_rx(struct mt76x02_dev *dev, struct sk_buff *skb,
 
        status->chains = BIT(0);
        signal = mt76x02_mac_get_rssi(dev, rxwi->rssi[0], 0);
-       for (i = 1; i < nstreams; i++) {
+       for (i = 0; i < nstreams; i++) {
                status->chains |= BIT(i);
                status->chain_signal[i] = mt76x02_mac_get_rssi(dev,
                                                               rxwi->rssi[i],
@@ -658,12 +662,7 @@ int mt76x02_mac_process_rx(struct mt76x02_dev *dev, struct sk_buff *skb,
        status->tid = FIELD_GET(MT_RXWI_TID, tid_sn);
        status->seqno = FIELD_GET(MT_RXWI_SN, tid_sn);
 
-       if (sta) {
-               ewma_signal_add(&sta->rssi, status->signal);
-               sta->inactive_count = 0;
-       }
-
-       return mt76x02_mac_process_rate(status, rate);
+       return mt76x02_mac_process_rate(dev, status, rate);
 }
 
 void mt76x02_mac_poll_tx_status(struct mt76x02_dev *dev, bool irq)
@@ -715,7 +714,7 @@ void mt76x02_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q,
 }
 EXPORT_SYMBOL_GPL(mt76x02_tx_complete_skb);
 
-void mt76x02_mac_set_tx_protection(struct mt76x02_dev *dev, u32 val)
+void mt76x02_mac_set_rts_thresh(struct mt76x02_dev *dev, u32 val)
 {
        u32 data = 0;
 
@@ -729,20 +728,89 @@ void mt76x02_mac_set_tx_protection(struct mt76x02_dev *dev, u32 val)
                 MT_PROT_CFG_CTRL | MT_PROT_CFG_RTS_THRESH, data);
        mt76_rmw(dev, MT_OFDM_PROT_CFG,
                 MT_PROT_CFG_CTRL | MT_PROT_CFG_RTS_THRESH, data);
-       mt76_rmw(dev, MT_MM20_PROT_CFG,
-                MT_PROT_CFG_CTRL | MT_PROT_CFG_RTS_THRESH, data);
-       mt76_rmw(dev, MT_MM40_PROT_CFG,
-                MT_PROT_CFG_CTRL | MT_PROT_CFG_RTS_THRESH, data);
-       mt76_rmw(dev, MT_GF20_PROT_CFG,
-                MT_PROT_CFG_CTRL | MT_PROT_CFG_RTS_THRESH, data);
-       mt76_rmw(dev, MT_GF40_PROT_CFG,
-                MT_PROT_CFG_CTRL | MT_PROT_CFG_RTS_THRESH, data);
-       mt76_rmw(dev, MT_TX_PROT_CFG6,
-                MT_PROT_CFG_CTRL | MT_PROT_CFG_RTS_THRESH, data);
-       mt76_rmw(dev, MT_TX_PROT_CFG7,
-                MT_PROT_CFG_CTRL | MT_PROT_CFG_RTS_THRESH, data);
-       mt76_rmw(dev, MT_TX_PROT_CFG8,
-                MT_PROT_CFG_CTRL | MT_PROT_CFG_RTS_THRESH, data);
+}
+
+void mt76x02_mac_set_tx_protection(struct mt76x02_dev *dev, bool legacy_prot,
+                                  int ht_mode)
+{
+       int mode = ht_mode & IEEE80211_HT_OP_MODE_PROTECTION;
+       bool non_gf = !!(ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+       u32 prot[6];
+       u32 vht_prot[3];
+       int i;
+       u16 rts_thr;
+
+       for (i = 0; i < ARRAY_SIZE(prot); i++) {
+               prot[i] = mt76_rr(dev, MT_CCK_PROT_CFG + i * 4);
+               prot[i] &= ~MT_PROT_CFG_CTRL;
+               if (i >= 2)
+                       prot[i] &= ~MT_PROT_CFG_RATE;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(vht_prot); i++) {
+               vht_prot[i] = mt76_rr(dev, MT_TX_PROT_CFG6 + i * 4);
+               vht_prot[i] &= ~(MT_PROT_CFG_CTRL | MT_PROT_CFG_RATE);
+       }
+
+       rts_thr = mt76_get_field(dev, MT_TX_RTS_CFG, MT_TX_RTS_CFG_THRESH);
+
+       if (rts_thr != 0xffff)
+               prot[0] |= MT_PROT_CTRL_RTS_CTS;
+
+       if (legacy_prot) {
+               prot[1] |= MT_PROT_CTRL_CTS2SELF;
+
+               prot[2] |= MT_PROT_RATE_CCK_11;
+               prot[3] |= MT_PROT_RATE_CCK_11;
+               prot[4] |= MT_PROT_RATE_CCK_11;
+               prot[5] |= MT_PROT_RATE_CCK_11;
+
+               vht_prot[0] |= MT_PROT_RATE_CCK_11;
+               vht_prot[1] |= MT_PROT_RATE_CCK_11;
+               vht_prot[2] |= MT_PROT_RATE_CCK_11;
+       } else {
+               if (rts_thr != 0xffff)
+                       prot[1] |= MT_PROT_CTRL_RTS_CTS;
+
+               prot[2] |= MT_PROT_RATE_OFDM_24;
+               prot[3] |= MT_PROT_RATE_DUP_OFDM_24;
+               prot[4] |= MT_PROT_RATE_OFDM_24;
+               prot[5] |= MT_PROT_RATE_DUP_OFDM_24;
+
+               vht_prot[0] |= MT_PROT_RATE_OFDM_24;
+               vht_prot[1] |= MT_PROT_RATE_DUP_OFDM_24;
+               vht_prot[2] |= MT_PROT_RATE_SGI_OFDM_24;
+       }
+
+       switch (mode) {
+       case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
+       case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
+               prot[2] |= MT_PROT_CTRL_RTS_CTS;
+               prot[3] |= MT_PROT_CTRL_RTS_CTS;
+               prot[4] |= MT_PROT_CTRL_RTS_CTS;
+               prot[5] |= MT_PROT_CTRL_RTS_CTS;
+               vht_prot[0] |= MT_PROT_CTRL_RTS_CTS;
+               vht_prot[1] |= MT_PROT_CTRL_RTS_CTS;
+               vht_prot[2] |= MT_PROT_CTRL_RTS_CTS;
+               break;
+       case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
+               prot[3] |= MT_PROT_CTRL_RTS_CTS;
+               prot[5] |= MT_PROT_CTRL_RTS_CTS;
+               vht_prot[1] |= MT_PROT_CTRL_RTS_CTS;
+               vht_prot[2] |= MT_PROT_CTRL_RTS_CTS;
+               break;
+       }
+
+       if (non_gf) {
+               prot[4] |= MT_PROT_CTRL_RTS_CTS;
+               prot[5] |= MT_PROT_CTRL_RTS_CTS;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(prot); i++)
+               mt76_wr(dev, MT_CCK_PROT_CFG + i * 4, prot[i]);
+
+       for (i = 0; i < ARRAY_SIZE(vht_prot); i++)
+               mt76_wr(dev, MT_TX_PROT_CFG6 + i * 4, vht_prot[i]);
 }
 
 void mt76x02_update_channel(struct mt76_dev *mdev)
@@ -774,8 +842,90 @@ static void mt76x02_check_mac_err(struct mt76x02_dev *dev)
 
        mt76_set(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_RESET_CSR);
        udelay(10);
-       mt76_clear(dev, MT_MAC_SYS_CTRL,
-                  MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX);
+       mt76_wr(dev, MT_MAC_SYS_CTRL,
+               MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX);
+}
+
+static void
+mt76x02_edcca_tx_enable(struct mt76x02_dev *dev, bool enable)
+{
+       if (enable) {
+               u32 data;
+
+               mt76_set(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX);
+               mt76_set(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_EN);
+               /* enable pa-lna */
+               data = mt76_rr(dev, MT_TX_PIN_CFG);
+               data |= MT_TX_PIN_CFG_TXANT |
+                       MT_TX_PIN_CFG_RXANT |
+                       MT_TX_PIN_RFTR_EN |
+                       MT_TX_PIN_TRSW_EN;
+               mt76_wr(dev, MT_TX_PIN_CFG, data);
+       } else {
+               mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX);
+               mt76_clear(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_EN);
+               /* disable pa-lna */
+               mt76_clear(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT);
+               mt76_clear(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_RXANT);
+       }
+       dev->ed_tx_blocked = !enable;
+}
+
+void mt76x02_edcca_init(struct mt76x02_dev *dev)
+{
+       dev->ed_trigger = 0;
+       dev->ed_silent = 0;
+
+       if (dev->ed_monitor) {
+               struct ieee80211_channel *chan = dev->mt76.chandef.chan;
+               u8 ed_th = chan->band == NL80211_BAND_5GHZ ? 0x0e : 0x20;
+
+               mt76_clear(dev, MT_TX_LINK_CFG, MT_TX_CFACK_EN);
+               mt76_set(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
+               mt76_rmw(dev, MT_BBP(AGC, 2), GENMASK(15, 0),
+                        ed_th << 8 | ed_th);
+               if (!is_mt76x2(dev))
+                       mt76_set(dev, MT_TXOP_HLDR_ET,
+                                MT_TXOP_HLDR_TX40M_BLK_EN);
+       } else {
+               mt76_set(dev, MT_TX_LINK_CFG, MT_TX_CFACK_EN);
+               mt76_clear(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
+               if (is_mt76x2(dev)) {
+                       mt76_wr(dev, MT_BBP(AGC, 2), 0x00007070);
+               } else {
+                       mt76_wr(dev, MT_BBP(AGC, 2), 0x003a6464);
+                       mt76_clear(dev, MT_TXOP_HLDR_ET,
+                                  MT_TXOP_HLDR_TX40M_BLK_EN);
+               }
+       }
+       mt76x02_edcca_tx_enable(dev, true);
+}
+EXPORT_SYMBOL_GPL(mt76x02_edcca_init);
+
+#define MT_EDCCA_TH            90
+#define MT_EDCCA_BLOCK_TH      2
+static void mt76x02_edcca_check(struct mt76x02_dev *dev)
+{
+       u32 val, busy;
+
+       val = mt76_rr(dev, MT_ED_CCA_TIMER);
+       busy = (val * 100) / jiffies_to_usecs(MT_CALIBRATE_INTERVAL);
+       busy = min_t(u32, busy, 100);
+
+       if (busy > MT_EDCCA_TH) {
+               dev->ed_trigger++;
+               dev->ed_silent = 0;
+       } else {
+               dev->ed_silent++;
+               dev->ed_trigger = 0;
+       }
+
+       if (dev->ed_trigger > MT_EDCCA_BLOCK_TH &&
+           !dev->ed_tx_blocked)
+               mt76x02_edcca_tx_enable(dev, false);
+       else if (dev->ed_silent > MT_EDCCA_BLOCK_TH &&
+                dev->ed_tx_blocked)
+               mt76x02_edcca_tx_enable(dev, true);
 }
 
 void mt76x02_mac_work(struct work_struct *work)
@@ -784,6 +934,8 @@ void mt76x02_mac_work(struct work_struct *work)
                                               mac_work.work);
        int i, idx;
 
+       mutex_lock(&dev->mt76.mutex);
+
        mt76x02_update_channel(&dev->mt76);
        for (i = 0, idx = 0; i < 16; i++) {
                u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i));
@@ -792,10 +944,14 @@ void mt76x02_mac_work(struct work_struct *work)
                dev->aggr_stats[idx++] += val >> 16;
        }
 
-       /* XXX: check beacon stuck for ap mode */
        if (!dev->beacon_mask)
                mt76x02_check_mac_err(dev);
 
+       if (dev->ed_monitor)
+               mt76x02_edcca_check(dev);
+
+       mutex_unlock(&dev->mt76.mutex);
+
        mt76_tx_status_check(&dev->mt76, NULL, false);
 
        ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work,
index 4e59700..940c07f 100644 (file)
@@ -18,8 +18,6 @@
 #ifndef __MT76X02_MAC_H
 #define __MT76X02_MAC_H
 
-#include <linux/average.h>
-
 struct mt76x02_dev;
 
 struct mt76x02_tx_status {
@@ -41,8 +39,6 @@ struct mt76x02_vif {
        u8 idx;
 };
 
-DECLARE_EWMA(signal, 10, 8);
-
 struct mt76x02_sta {
        struct mt76_wcid wcid; /* must be first */
 
@@ -50,8 +46,6 @@ struct mt76x02_sta {
        struct mt76x02_tx_status status;
        int n_frames;
 
-       struct ewma_signal rssi;
-       int inactive_count;
 };
 
 #define MT_RXINFO_BA                   BIT(0)
@@ -194,7 +188,9 @@ void mt76x02_send_tx_status(struct mt76x02_dev *dev,
                            struct mt76x02_tx_status *stat, u8 *update);
 int mt76x02_mac_process_rx(struct mt76x02_dev *dev, struct sk_buff *skb,
                           void *rxi);
-void mt76x02_mac_set_tx_protection(struct mt76x02_dev *dev, u32 val);
+void mt76x02_mac_set_tx_protection(struct mt76x02_dev *dev, bool legacy_prot,
+                                  int ht_mode);
+void mt76x02_mac_set_rts_thresh(struct mt76x02_dev *dev, u32 val);
 void mt76x02_mac_setaddr(struct mt76x02_dev *dev, u8 *addr);
 void mt76x02_mac_write_txwi(struct mt76x02_dev *dev, struct mt76x02_txwi *txwi,
                            struct sk_buff *skb, struct mt76_wcid *wcid,
@@ -210,4 +206,6 @@ int mt76x02_mac_set_beacon(struct mt76x02_dev *dev, u8 vif_idx,
                           struct sk_buff *skb);
 void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev, u8 vif_idx,
                                   bool val);
+
+void mt76x02_edcca_init(struct mt76x02_dev *dev);
 #endif
index 6631541..374bc9d 100644 (file)
@@ -116,14 +116,20 @@ static void mt76x02_pre_tbtt_tasklet(unsigned long arg)
                IEEE80211_IFACE_ITER_RESUME_ALL,
                mt76x02_update_beacon_iter, dev);
 
+       mt76_csa_check(&dev->mt76);
+
+       if (dev->mt76.csa_complete)
+               return;
+
        do {
                nframes = skb_queue_len(&data.q);
                ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
                        IEEE80211_IFACE_ITER_RESUME_ALL,
                        mt76x02_add_buffered_bc, &data);
-       } while (nframes != skb_queue_len(&data.q));
+       } while (nframes != skb_queue_len(&data.q) &&
+                skb_queue_len(&data.q) < 8);
 
-       if (!nframes)
+       if (!skb_queue_len(&data.q))
                return;
 
        for (i = 0; i < ARRAY_SIZE(data.tail); i++) {
@@ -308,8 +314,12 @@ irqreturn_t mt76x02_irq_handler(int irq, void *dev_instance)
                tasklet_schedule(&dev->pre_tbtt_tasklet);
 
        /* send buffered multicast frames now */
-       if (intr & MT_INT_TBTT)
-               mt76_queue_kick(dev, &dev->mt76.q_tx[MT_TXQ_PSD]);
+       if (intr & MT_INT_TBTT) {
+               if (dev->mt76.csa_complete)
+                       mt76_csa_finish(&dev->mt76);
+               else
+                       mt76_queue_kick(dev, &dev->mt76.q_tx[MT_TXQ_PSD]);
+       }
 
        if (intr & MT_INT_TX_STAT) {
                mt76x02_mac_poll_tx_status(dev, true);
@@ -384,3 +394,127 @@ void mt76x02_mac_start(struct mt76x02_dev *dev)
                           MT_INT_TX_STAT);
 }
 EXPORT_SYMBOL_GPL(mt76x02_mac_start);
+
+static bool mt76x02_tx_hang(struct mt76x02_dev *dev)
+{
+       u32 dma_idx, prev_dma_idx;
+       struct mt76_queue *q;
+       int i;
+
+       for (i = 0; i < 4; i++) {
+               q = &dev->mt76.q_tx[i];
+
+               if (!q->queued)
+                       continue;
+
+               prev_dma_idx = dev->mt76.tx_dma_idx[i];
+               dma_idx = ioread32(&q->regs->dma_idx);
+               dev->mt76.tx_dma_idx[i] = dma_idx;
+
+               if (prev_dma_idx == dma_idx)
+                       break;
+       }
+
+       return i < 4;
+}
+
+static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
+{
+       u32 mask = dev->mt76.mmio.irqmask;
+       int i;
+
+       ieee80211_stop_queues(dev->mt76.hw);
+       set_bit(MT76_RESET, &dev->mt76.state);
+
+       tasklet_disable(&dev->pre_tbtt_tasklet);
+       tasklet_disable(&dev->tx_tasklet);
+
+       for (i = 0; i < ARRAY_SIZE(dev->mt76.napi); i++)
+               napi_disable(&dev->mt76.napi[i]);
+
+       mutex_lock(&dev->mt76.mutex);
+
+       if (dev->beacon_mask)
+               mt76_clear(dev, MT_BEACON_TIME_CFG,
+                          MT_BEACON_TIME_CFG_BEACON_TX |
+                          MT_BEACON_TIME_CFG_TBTT_EN);
+
+       mt76x02_irq_disable(dev, mask);
+
+       /* perform device reset */
+       mt76_clear(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
+       mt76_wr(dev, MT_MAC_SYS_CTRL, 0);
+       mt76_clear(dev, MT_WPDMA_GLO_CFG,
+                  MT_WPDMA_GLO_CFG_TX_DMA_EN | MT_WPDMA_GLO_CFG_RX_DMA_EN);
+       usleep_range(5000, 10000);
+       mt76_wr(dev, MT_INT_SOURCE_CSR, 0xffffffff);
+
+       /* let fw reset DMA */
+       mt76_set(dev, 0x734, 0x3);
+
+       for (i = 0; i < ARRAY_SIZE(dev->mt76.q_tx); i++)
+               mt76_queue_tx_cleanup(dev, i, true);
+
+       for (i = 0; i < ARRAY_SIZE(dev->mt76.q_rx); i++)
+               mt76_queue_rx_reset(dev, i);
+
+       mt76_wr(dev, MT_MAC_SYS_CTRL,
+               MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX);
+       mt76_set(dev, MT_WPDMA_GLO_CFG,
+                MT_WPDMA_GLO_CFG_TX_DMA_EN | MT_WPDMA_GLO_CFG_RX_DMA_EN);
+       if (dev->ed_monitor)
+               mt76_set(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN);
+
+       if (dev->beacon_mask)
+               mt76_set(dev, MT_BEACON_TIME_CFG,
+                        MT_BEACON_TIME_CFG_BEACON_TX |
+                        MT_BEACON_TIME_CFG_TBTT_EN);
+
+       mt76x02_irq_enable(dev, mask);
+
+       mutex_unlock(&dev->mt76.mutex);
+
+       clear_bit(MT76_RESET, &dev->mt76.state);
+
+       tasklet_enable(&dev->tx_tasklet);
+       tasklet_schedule(&dev->tx_tasklet);
+
+       tasklet_enable(&dev->pre_tbtt_tasklet);
+
+       for (i = 0; i < ARRAY_SIZE(dev->mt76.napi); i++) {
+               napi_enable(&dev->mt76.napi[i]);
+               napi_schedule(&dev->mt76.napi[i]);
+       }
+
+       ieee80211_wake_queues(dev->mt76.hw);
+
+       mt76_txq_schedule_all(&dev->mt76);
+}
+
+static void mt76x02_check_tx_hang(struct mt76x02_dev *dev)
+{
+       if (mt76x02_tx_hang(dev)) {
+               if (++dev->tx_hang_check < MT_TX_HANG_TH)
+                       return;
+
+               mt76x02_watchdog_reset(dev);
+
+               dev->tx_hang_reset++;
+               dev->tx_hang_check = 0;
+               memset(dev->mt76.tx_dma_idx, 0xff,
+                      sizeof(dev->mt76.tx_dma_idx));
+       } else {
+               dev->tx_hang_check = 0;
+       }
+}
+
+void mt76x02_wdt_work(struct work_struct *work)
+{
+       struct mt76x02_dev *dev = container_of(work, struct mt76x02_dev,
+                                              wdt_work.work);
+
+       mt76x02_check_tx_hang(dev);
+
+       ieee80211_queue_delayed_work(mt76_hw(dev), &dev->wdt_work,
+                                    MT_WATCHDOG_TIME);
+}
index 977a8e7..a020c75 100644 (file)
@@ -132,53 +132,6 @@ void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_1)
 }
 EXPORT_SYMBOL_GPL(mt76x02_phy_set_txpower);
 
-int mt76x02_phy_get_min_avg_rssi(struct mt76x02_dev *dev)
-{
-       struct mt76x02_sta *sta;
-       struct mt76_wcid *wcid;
-       int i, j, min_rssi = 0;
-       s8 cur_rssi;
-
-       local_bh_disable();
-       rcu_read_lock();
-
-       for (i = 0; i < ARRAY_SIZE(dev->mt76.wcid_mask); i++) {
-               unsigned long mask = dev->mt76.wcid_mask[i];
-
-               if (!mask)
-                       continue;
-
-               for (j = i * BITS_PER_LONG; mask; j++, mask >>= 1) {
-                       if (!(mask & 1))
-                               continue;
-
-                       wcid = rcu_dereference(dev->mt76.wcid[j]);
-                       if (!wcid)
-                               continue;
-
-                       sta = container_of(wcid, struct mt76x02_sta, wcid);
-                       spin_lock(&dev->mt76.rx_lock);
-                       if (sta->inactive_count++ < 5)
-                               cur_rssi = ewma_signal_read(&sta->rssi);
-                       else
-                               cur_rssi = 0;
-                       spin_unlock(&dev->mt76.rx_lock);
-
-                       if (cur_rssi < min_rssi)
-                               min_rssi = cur_rssi;
-               }
-       }
-
-       rcu_read_unlock();
-       local_bh_enable();
-
-       if (!min_rssi)
-               return -75;
-
-       return min_rssi;
-}
-EXPORT_SYMBOL_GPL(mt76x02_phy_get_min_avg_rssi);
-
 void mt76x02_phy_set_bw(struct mt76x02_dev *dev, int width, u8 ctrl)
 {
        int core_val, agc_val;
index 2b316cf..d2971db 100644 (file)
@@ -51,7 +51,6 @@ void mt76x02_limit_rate_power(struct mt76_rate_power *r, int limit);
 int mt76x02_get_max_rate_power(struct mt76_rate_power *r);
 void mt76x02_phy_set_rxpath(struct mt76x02_dev *dev);
 void mt76x02_phy_set_txdac(struct mt76x02_dev *dev);
-int mt76x02_phy_get_min_avg_rssi(struct mt76x02_dev *dev);
 void mt76x02_phy_set_bw(struct mt76x02_dev *dev, int width, u8 ctrl);
 void mt76x02_phy_set_band(struct mt76x02_dev *dev, int band,
                          bool primary_upper);
index f7de77d..7401cb9 100644 (file)
 #define MT_COM_REG2                    0x0738
 #define MT_COM_REG3                    0x073C
 
+#define MT_LED_CTRL                    0x0770
+#define MT_LED_CTRL_REPLAY(_n)         BIT(0 + (8 * (_n)))
+#define MT_LED_CTRL_POLARITY(_n)       BIT(1 + (8 * (_n)))
+#define MT_LED_CTRL_TX_BLINK_MODE(_n)  BIT(2 + (8 * (_n)))
+#define MT_LED_CTRL_KICK(_n)           BIT(7 + (8 * (_n)))
+
+#define MT_LED_TX_BLINK_0              0x0774
+#define MT_LED_TX_BLINK_1              0x0778
+
+#define MT_LED_S0_BASE                 0x077C
+#define MT_LED_S0(_n)                  (MT_LED_S0_BASE + 8 * (_n))
+#define MT_LED_S1_BASE                 0x0780
+#define MT_LED_S1(_n)                  (MT_LED_S1_BASE + 8 * (_n))
+#define MT_LED_STATUS_OFF_MASK         GENMASK(31, 24)
+#define MT_LED_STATUS_OFF(_v)          (((_v) << __ffs(MT_LED_STATUS_OFF_MASK)) & \
+                                        MT_LED_STATUS_OFF_MASK)
+#define MT_LED_STATUS_ON_MASK          GENMASK(23, 16)
+#define MT_LED_STATUS_ON(_v)           (((_v) << __ffs(MT_LED_STATUS_ON_MASK)) & \
+                                        MT_LED_STATUS_ON_MASK)
+#define MT_LED_STATUS_DURATION_MASK    GENMASK(15, 8)
+#define MT_LED_STATUS_DURATION(_v)     (((_v) << __ffs(MT_LED_STATUS_DURATION_MASK)) & \
+                                        MT_LED_STATUS_DURATION_MASK)
+
 #define MT_FCE_PSE_CTRL                        0x0800
 #define MT_FCE_PARAMETERS              0x0804
 #define MT_FCE_CSO                     0x0808
 #define MT_CH_TIME_CFG_NAV_AS_BUSY     BIT(3)
 #define MT_CH_TIME_CFG_EIFS_AS_BUSY    BIT(4)
 #define MT_CH_TIME_CFG_MDRDY_CNT_EN    BIT(5)
+#define MT_CH_CCA_RC_EN                        BIT(6)
 #define MT_CH_TIME_CFG_CH_TIMER_CLR    GENMASK(9, 8)
 #define MT_CH_TIME_CFG_MDRDY_CLR       GENMASK(11, 10)
 
 #define MT_TX_PWR_CFG_4                        0x1324
 #define MT_TX_PIN_CFG                  0x1328
 #define MT_TX_PIN_CFG_TXANT            GENMASK(3, 0)
+#define MT_TX_PIN_CFG_RXANT            GENMASK(11, 8)
+#define MT_TX_PIN_RFTR_EN              BIT(16)
+#define MT_TX_PIN_TRSW_EN              BIT(18)
 
 #define MT_TX_BAND_CFG                 0x132c
 #define MT_TX_BAND_CFG_UPPER_40M       BIT(0)
 #define MT_TXOP_CTRL_CFG               0x1340
 #define MT_TXOP_TRUN_EN                        GENMASK(5, 0)
 #define MT_TXOP_EXT_CCA_DLY            GENMASK(15, 8)
+#define MT_TXOP_ED_CCA_EN              BIT(20)
 
 #define MT_TX_RTS_CFG                  0x1344
 #define MT_TX_RTS_CFG_RETRY_LIMIT      GENMASK(7, 0)
 
 #define MT_TX_RETRY_CFG                        0x134c
 #define MT_TX_LINK_CFG                 0x1350
+#define MT_TX_CFACK_EN                 BIT(12)
 #define MT_VHT_HT_FBK_CFG0             0x1354
 #define MT_VHT_HT_FBK_CFG1             0x1358
 #define MT_LG_FBK_CFG0                 0x135c
 #define MT_PROT_TXOP_ALLOW_GF40                BIT(25)
 #define MT_PROT_RTS_THR_EN             BIT(26)
 #define MT_PROT_RATE_CCK_11            0x0003
-#define MT_PROT_RATE_OFDM_6            0x4000
-#define MT_PROT_RATE_OFDM_24           0x4004
-#define MT_PROT_RATE_DUP_OFDM_24       0x4084
+#define MT_PROT_RATE_OFDM_6            0x2000
+#define MT_PROT_RATE_OFDM_24           0x2004
+#define MT_PROT_RATE_DUP_OFDM_24       0x2084
+#define MT_PROT_RATE_SGI_OFDM_24       0x2104
 #define MT_PROT_TXOP_ALLOW_ALL         GENMASK(25, 20)
 #define MT_PROT_TXOP_ALLOW_BW20                (MT_PROT_TXOP_ALLOW_ALL &       \
                                         ~MT_PROT_TXOP_ALLOW_MM40 &     \
 #define MT_RX_FILTR_CFG_CTRL_RSV       BIT(16)
 
 #define MT_AUTO_RSP_CFG                        0x1404
+#define MT_AUTO_RSP_EN                 BIT(0)
 #define MT_AUTO_RSP_PREAMB_SHORT       BIT(4)
 #define MT_LEGACY_BASIC_RATE           0x1408
 #define MT_HT_BASIC_RATE               0x140c
 #define MT_PN_PAD_MODE                 0x150c
 
 #define MT_TXOP_HLDR_ET                        0x1608
+#define MT_TXOP_HLDR_TX40M_BLK_EN      BIT(1)
 
 #define MT_PROT_AUTO_TX_CFG            0x1648
 #define MT_PROT_AUTO_TX_CFG_PROT_PADJ  GENMASK(11, 8)
index 4598cb2..a5413a3 100644 (file)
@@ -177,7 +177,7 @@ int mt76x02_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
        if (ret < 0)
                return ret;
 
-       if (pid && pid != MT_PACKET_ID_NO_ACK)
+       if (pid >= MT_PACKET_ID_FIRST)
                qsel = MT_QSEL_MGMT;
 
        *tx_info = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) |
index 81970cf..098d05e 100644 (file)
@@ -87,8 +87,7 @@ int mt76x02u_tx_prepare_skb(struct mt76_dev *mdev, void *data,
        pid = mt76_tx_status_skb_add(mdev, wcid, skb);
        txwi->pktid = pid;
 
-       if ((pid && pid != MT_PACKET_ID_NO_ACK) ||
-           q2ep(q->hw_idx) == MT_EP_OUT_HCCA)
+       if (pid >= MT_PACKET_ID_FIRST || q2ep(q->hw_idx) == MT_EP_OUT_HCCA)
                qsel = MT_QSEL_MGMT;
        else
                qsel = MT_QSEL_EDCA;
index 38bd466..062614a 100644 (file)
@@ -75,6 +75,58 @@ static const struct ieee80211_iface_combination mt76x02_if_comb[] = {
        }
 };
 
+static void
+mt76x02_led_set_config(struct mt76_dev *mdev, u8 delay_on,
+                      u8 delay_off)
+{
+       struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev,
+                                              mt76);
+       u32 val;
+
+       val = MT_LED_STATUS_DURATION(0xff) |
+             MT_LED_STATUS_OFF(delay_off) |
+             MT_LED_STATUS_ON(delay_on);
+
+       mt76_wr(dev, MT_LED_S0(mdev->led_pin), val);
+       mt76_wr(dev, MT_LED_S1(mdev->led_pin), val);
+
+       val = MT_LED_CTRL_REPLAY(mdev->led_pin) |
+             MT_LED_CTRL_KICK(mdev->led_pin);
+       if (mdev->led_al)
+               val |= MT_LED_CTRL_POLARITY(mdev->led_pin);
+       mt76_wr(dev, MT_LED_CTRL, val);
+}
+
+static int
+mt76x02_led_set_blink(struct led_classdev *led_cdev,
+                     unsigned long *delay_on,
+                     unsigned long *delay_off)
+{
+       struct mt76_dev *mdev = container_of(led_cdev, struct mt76_dev,
+                                            led_cdev);
+       u8 delta_on, delta_off;
+
+       delta_off = max_t(u8, *delay_off / 10, 1);
+       delta_on = max_t(u8, *delay_on / 10, 1);
+
+       mt76x02_led_set_config(mdev, delta_on, delta_off);
+
+       return 0;
+}
+
+static void
+mt76x02_led_set_brightness(struct led_classdev *led_cdev,
+                          enum led_brightness brightness)
+{
+       struct mt76_dev *mdev = container_of(led_cdev, struct mt76_dev,
+                                            led_cdev);
+
+       if (!brightness)
+               mt76x02_led_set_config(mdev, 0, 0xff);
+       else
+               mt76x02_led_set_config(mdev, 0xff, 0);
+}
+
 void mt76x02_init_device(struct mt76x02_dev *dev)
 {
        struct ieee80211_hw *hw = mt76_hw(dev);
@@ -93,6 +145,8 @@ void mt76x02_init_device(struct mt76x02_dev *dev)
                                         MT_DMA_HDR_LEN;
                wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
        } else {
+               INIT_DELAYED_WORK(&dev->wdt_work, mt76x02_wdt_work);
+
                mt76x02_dfs_init_detector(dev);
 
                wiphy->reg_notifier = mt76x02_regd_notifier;
@@ -106,7 +160,16 @@ void mt76x02_init_device(struct mt76x02_dev *dev)
 #endif
                        BIT(NL80211_IFTYPE_ADHOC);
 
+               wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+
                wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
+
+               /* init led callbacks */
+               if (IS_ENABLED(CONFIG_MT76_LEDS)) {
+                       dev->mt76.led_cdev.brightness_set =
+                                       mt76x02_led_set_brightness;
+                       dev->mt76.led_cdev.blink_set = mt76x02_led_set_blink;
+               }
        }
 
        hw->sta_data_size = sizeof(struct mt76x02_sta);
@@ -189,8 +252,6 @@ int mt76x02_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
        if (vif->type == NL80211_IFTYPE_AP)
                set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags);
 
-       ewma_signal_init(&msta->rssi);
-
        return 0;
 }
 EXPORT_SYMBOL_GPL(mt76x02_sta_add);
@@ -463,7 +524,7 @@ int mt76x02_set_rts_threshold(struct ieee80211_hw *hw, u32 val)
                return -EINVAL;
 
        mutex_lock(&dev->mt76.mutex);
-       mt76x02_mac_set_tx_protection(dev, val);
+       mt76x02_mac_set_rts_thresh(dev, val);
        mutex_unlock(&dev->mt76.mutex);
 
        return 0;
@@ -546,24 +607,6 @@ void mt76x02_sw_scan_complete(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL_GPL(mt76x02_sw_scan_complete);
 
-int mt76x02_get_txpower(struct ieee80211_hw *hw,
-                       struct ieee80211_vif *vif, int *dbm)
-{
-       struct mt76x02_dev *dev = hw->priv;
-       u8 nstreams = dev->mt76.chainmask & 0xf;
-
-       *dbm = dev->mt76.txpower_cur / 2;
-
-       /* convert from per-chain power to combined
-        * output on 2x2 devices
-        */
-       if (nstreams > 1)
-               *dbm += 3;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(mt76x02_get_txpower);
-
 void mt76x02_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta,
                    bool ps)
 {
@@ -661,6 +704,10 @@ void mt76x02_bss_info_changed(struct ieee80211_hw *hw,
                tasklet_enable(&dev->pre_tbtt_tasklet);
        }
 
+       if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT)
+               mt76x02_mac_set_tx_protection(dev, info->use_cts_prot,
+                                             info->ht_operation_mode);
+
        if (changed & BSS_CHANGED_BEACON_INT) {
                mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
                               MT_BEACON_TIME_CFG_INTVAL,
index 54a9b5f..f853436 100644 (file)
@@ -143,6 +143,7 @@ void mt76_write_mac_initvals(struct mt76x02_dev *dev)
                { MT_VHT_HT_FBK_CFG1,           0xedcba980 },
                { MT_PROT_AUTO_TX_CFG,          0x00830083 },
                { MT_HT_CTRL_CFG,               0x000001ff },
+               { MT_TX_LINK_CFG,               0x00001020 },
        };
        struct mt76_reg_pair prot_vals[] = {
                { MT_CCK_PROT_CFG,              DEFAULT_PROT_CFG_CCK },
index acfa2b5..40ef439 100644 (file)
 #define MT_MCU_PCIE_REMAP_BASE2                0x0744
 #define MT_MCU_PCIE_REMAP_BASE3                0x0748
 
-#define MT_LED_CTRL                    0x0770
-#define MT_LED_CTRL_REPLAY(_n)         BIT(0 + (8 * (_n)))
-#define MT_LED_CTRL_POLARITY(_n)       BIT(1 + (8 * (_n)))
-#define MT_LED_CTRL_TX_BLINK_MODE(_n)  BIT(2 + (8 * (_n)))
-#define MT_LED_CTRL_KICK(_n)           BIT(7 + (8 * (_n)))
-
-#define MT_LED_TX_BLINK_0              0x0774
-#define MT_LED_TX_BLINK_1              0x0778
-
-#define MT_LED_S0_BASE                 0x077C
-#define MT_LED_S0(_n)                  (MT_LED_S0_BASE + 8 * (_n))
-#define MT_LED_S1_BASE                 0x0780
-#define MT_LED_S1(_n)                  (MT_LED_S1_BASE + 8 * (_n))
-#define MT_LED_STATUS_OFF_MASK         GENMASK(31, 24)
-#define MT_LED_STATUS_OFF(_v)          (((_v) << __ffs(MT_LED_STATUS_OFF_MASK)) & \
-                                        MT_LED_STATUS_OFF_MASK)
-#define MT_LED_STATUS_ON_MASK          GENMASK(23, 16)
-#define MT_LED_STATUS_ON(_v)           (((_v) << __ffs(MT_LED_STATUS_ON_MASK)) & \
-                                        MT_LED_STATUS_ON_MASK)
-#define MT_LED_STATUS_DURATION_MASK    GENMASK(15, 8)
-#define MT_LED_STATUS_DURATION(_v)     (((_v) << __ffs(MT_LED_STATUS_DURATION_MASK)) & \
-                                        MT_LED_STATUS_DURATION_MASK)
-
 #define MT_MCU_ROM_PATCH_OFFSET                0x80000
 #define MT_MCU_ROM_PATCH_ADDR          0x90000
 
index b259e4b..28ec19a 100644 (file)
@@ -53,7 +53,6 @@ struct mt76x02_dev *mt76x2_alloc_device(struct device *pdev);
 int mt76x2_register_device(struct mt76x02_dev *dev);
 
 void mt76x2_phy_power_on(struct mt76x02_dev *dev);
-int mt76x2_init_hardware(struct mt76x02_dev *dev);
 void mt76x2_stop_hardware(struct mt76x02_dev *dev);
 int mt76x2_eeprom_init(struct mt76x02_dev *dev);
 int mt76x2_apply_calibration_data(struct mt76x02_dev *dev, int channel);
index 7f4ea2d..4347d5e 100644 (file)
@@ -151,6 +151,7 @@ static int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard)
                MT_CH_TIME_CFG_RX_AS_BUSY |
                MT_CH_TIME_CFG_NAV_AS_BUSY |
                MT_CH_TIME_CFG_EIFS_AS_BUSY |
+               MT_CH_CCA_RC_EN |
                FIELD_PREP(MT_CH_TIME_CFG_CH_TIMER_CLR, 1));
 
        mt76x02_set_tx_ackto(dev);
@@ -260,7 +261,7 @@ mt76x2_power_on(struct mt76x02_dev *dev)
        mt76x2_power_on_rf(dev, 1);
 }
 
-int mt76x2_init_hardware(struct mt76x02_dev *dev)
+static int mt76x2_init_hardware(struct mt76x02_dev *dev)
 {
        int ret;
 
@@ -300,6 +301,7 @@ void mt76x2_stop_hardware(struct mt76x02_dev *dev)
 {
        cancel_delayed_work_sync(&dev->cal_work);
        cancel_delayed_work_sync(&dev->mac_work);
+       cancel_delayed_work_sync(&dev->wdt_work);
        mt76x02_mcu_set_radio_state(dev, false);
        mt76x2_mac_stop(dev, false);
 }
@@ -340,54 +342,6 @@ struct mt76x02_dev *mt76x2_alloc_device(struct device *pdev)
        return dev;
 }
 
-static void mt76x2_led_set_config(struct mt76_dev *mt76, u8 delay_on,
-                                 u8 delay_off)
-{
-       struct mt76x02_dev *dev = container_of(mt76, struct mt76x02_dev,
-                                              mt76);
-       u32 val;
-
-       val = MT_LED_STATUS_DURATION(0xff) |
-             MT_LED_STATUS_OFF(delay_off) |
-             MT_LED_STATUS_ON(delay_on);
-
-       mt76_wr(dev, MT_LED_S0(mt76->led_pin), val);
-       mt76_wr(dev, MT_LED_S1(mt76->led_pin), val);
-
-       val = MT_LED_CTRL_REPLAY(mt76->led_pin) |
-             MT_LED_CTRL_KICK(mt76->led_pin);
-       if (mt76->led_al)
-               val |= MT_LED_CTRL_POLARITY(mt76->led_pin);
-       mt76_wr(dev, MT_LED_CTRL, val);
-}
-
-static int mt76x2_led_set_blink(struct led_classdev *led_cdev,
-                               unsigned long *delay_on,
-                               unsigned long *delay_off)
-{
-       struct mt76_dev *mt76 = container_of(led_cdev, struct mt76_dev,
-                                            led_cdev);
-       u8 delta_on, delta_off;
-
-       delta_off = max_t(u8, *delay_off / 10, 1);
-       delta_on = max_t(u8, *delay_on / 10, 1);
-
-       mt76x2_led_set_config(mt76, delta_on, delta_off);
-       return 0;
-}
-
-static void mt76x2_led_set_brightness(struct led_classdev *led_cdev,
-                                     enum led_brightness brightness)
-{
-       struct mt76_dev *mt76 = container_of(led_cdev, struct mt76_dev,
-                                            led_cdev);
-
-       if (!brightness)
-               mt76x2_led_set_config(mt76, 0, 0xff);
-       else
-               mt76x2_led_set_config(mt76, 0xff, 0);
-}
-
 int mt76x2_register_device(struct mt76x02_dev *dev)
 {
        int ret;
@@ -402,12 +356,6 @@ int mt76x2_register_device(struct mt76x02_dev *dev)
 
        mt76x02_config_mac_addr_list(dev);
 
-       /* init led callbacks */
-       if (IS_ENABLED(CONFIG_MT76_LEDS)) {
-               dev->mt76.led_cdev.brightness_set = mt76x2_led_set_brightness;
-               dev->mt76.led_cdev.blink_set = mt76x2_led_set_blink;
-       }
-
        ret = mt76_register_device(&dev->mt76, true, mt76x02_rates,
                                   ARRAY_SIZE(mt76x02_rates));
        if (ret)
index b54a323..06a26a1 100644 (file)
@@ -34,6 +34,8 @@ mt76x2_start(struct ieee80211_hw *hw)
 
        ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mac_work,
                                     MT_CALIBRATE_INTERVAL);
+       ieee80211_queue_delayed_work(mt76_hw(dev), &dev->wdt_work,
+                                    MT_WATCHDOG_TIME);
 
        set_bit(MT76_STATE_RUNNING, &dev->mt76.state);
 
@@ -189,7 +191,7 @@ const struct ieee80211_ops mt76x2_ops = {
        .sw_scan_complete = mt76x02_sw_scan_complete,
        .flush = mt76x2_flush,
        .ampdu_action = mt76x02_ampdu_action,
-       .get_txpower = mt76x02_get_txpower,
+       .get_txpower = mt76_get_txpower,
        .wake_tx_queue = mt76_wake_tx_queue,
        .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update,
        .release_buffered_frames = mt76_release_buffered_frames,
index da7cd40..65ed622 100644 (file)
@@ -254,6 +254,8 @@ int mt76x2_phy_set_channel(struct mt76x02_dev *dev,
                               0x38);
        }
 
+       mt76x02_edcca_init(dev);
+
        ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work,
                                     MT_CALIBRATE_INTERVAL);
 
index c9634a7..e2ee5e4 100644 (file)
@@ -284,7 +284,9 @@ void mt76x2_phy_update_channel_gain(struct mt76x02_dev *dev)
        int low_gain;
        u32 val;
 
-       dev->cal.avg_rssi_all = mt76x02_phy_get_min_avg_rssi(dev);
+       dev->cal.avg_rssi_all = mt76_get_min_avg_rssi(&dev->mt76);
+       if (!dev->cal.avg_rssi_all)
+               dev->cal.avg_rssi_all = -75;
 
        low_gain = (dev->cal.avg_rssi_all > mt76x02_get_rssi_gain_thresh(dev)) +
                   (dev->cal.avg_rssi_all > mt76x02_get_low_rssi_gain_thresh(dev));
index 2b48cc5..286c7f4 100644 (file)
@@ -138,5 +138,5 @@ const struct ieee80211_ops mt76x2u_ops = {
        .sw_scan_start = mt76x02_sw_scan,
        .sw_scan_complete = mt76x02_sw_scan_complete,
        .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update,
-       .get_txpower = mt76x02_get_txpower,
+       .get_txpower = mt76_get_txpower,
 };
index 45a95ee..152d41f 100644 (file)
@@ -39,7 +39,7 @@ static void mt76x2u_mcu_load_ivb(struct mt76x02_dev *dev)
 static void mt76x2u_mcu_enable_patch(struct mt76x02_dev *dev)
 {
        struct mt76_usb *usb = &dev->mt76.usb;
-       const u8 data[] = {
+       static const u8 data[] = {
                0x6f, 0xfc, 0x08, 0x01,
                0x20, 0x04, 0x00, 0x00,
                0x00, 0x09, 0x00,
index 7b71105..ef38e86 100644 (file)
@@ -170,21 +170,22 @@ mt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid,
        int pid;
 
        if (!wcid)
-               return 0;
+               return MT_PACKET_ID_NO_ACK;
 
        if (info->flags & IEEE80211_TX_CTL_NO_ACK)
                return MT_PACKET_ID_NO_ACK;
 
        if (!(info->flags & (IEEE80211_TX_CTL_REQ_TX_STATUS |
                             IEEE80211_TX_CTL_RATE_CTRL_PROBE)))
-               return 0;
+               return MT_PACKET_ID_NO_SKB;
 
        spin_lock_bh(&dev->status_list.lock);
 
        memset(cb, 0, sizeof(*cb));
        wcid->packet_id = (wcid->packet_id + 1) & MT_PACKET_ID_MASK;
-       if (!wcid->packet_id || wcid->packet_id == MT_PACKET_ID_NO_ACK)
-               wcid->packet_id = 1;
+       if (wcid->packet_id == MT_PACKET_ID_NO_ACK ||
+           wcid->packet_id == MT_PACKET_ID_NO_SKB)
+               wcid->packet_id = MT_PACKET_ID_FIRST;
 
        pid = wcid->packet_id;
        cb->wcid = wcid->idx;
@@ -330,7 +331,8 @@ mt76_queue_ps_skb(struct mt76_dev *dev, struct ieee80211_sta *sta,
 
        info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE;
        if (last)
-               info->flags |= IEEE80211_TX_STATUS_EOSP;
+               info->flags |= IEEE80211_TX_STATUS_EOSP |
+                              IEEE80211_TX_CTL_REQ_TX_STATUS;
 
        mt76_skb_set_moredata(skb, !last);
        dev->queue_ops->tx_queue_skb(dev, hwq, skb, wcid, sta);
@@ -394,6 +396,11 @@ mt76_txq_send_burst(struct mt76_dev *dev, struct mt76_queue *hwq,
        bool probe;
        int idx;
 
+       if (test_bit(MT_WCID_FLAG_PS, &wcid->flags)) {
+               *empty = true;
+               return 0;
+       }
+
        skb = mt76_txq_dequeue(dev, mtxq, false);
        if (!skb) {
                *empty = true;
index b061263..6a25075 100644 (file)
@@ -407,17 +407,15 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb)
        if (len < 0)
                return 0;
 
+       data_len = min_t(int, len, urb->sg[0].length - MT_DMA_HDR_LEN);
+       if (MT_DMA_HDR_LEN + data_len > SKB_WITH_OVERHEAD(q->buf_size))
+               return 0;
+
        skb = build_skb(data, q->buf_size);
        if (!skb)
                return 0;
 
-       data_len = min_t(int, len, urb->sg[0].length - MT_DMA_HDR_LEN);
        skb_reserve(skb, MT_DMA_HDR_LEN);
-       if (skb->tail + data_len > skb->end) {
-               dev_kfree_skb(skb);
-               return 1;
-       }
-
        __skb_put(skb, data_len);
        len -= data_len;
 
@@ -585,6 +583,7 @@ static void mt76u_stop_rx(struct mt76_dev *dev)
 static void mt76u_tx_tasklet(unsigned long data)
 {
        struct mt76_dev *dev = (struct mt76_dev *)data;
+       struct mt76_queue_entry entry;
        struct mt76u_buf *buf;
        struct mt76_queue *q;
        bool wake;
@@ -599,17 +598,18 @@ static void mt76u_tx_tasklet(unsigned long data)
                        if (!buf->done || !q->queued)
                                break;
 
-                       dev->drv->tx_complete_skb(dev, q,
-                                                 &q->entry[q->head],
-                                                 false);
-
                        if (q->entry[q->head].schedule) {
                                q->entry[q->head].schedule = false;
                                q->swq_queued--;
                        }
 
+                       entry = q->entry[q->head];
                        q->head = (q->head + 1) % q->ndesc;
                        q->queued--;
+
+                       spin_unlock_bh(&q->lock);
+                       dev->drv->tx_complete_skb(dev, q, &entry, false);
+                       spin_lock_bh(&q->lock);
                }
                mt76_txq_schedule(dev, q);
                wake = i < IEEE80211_NUM_ACS && q->queued < q->ndesc - 8;
index 0c35b8d..69270c1 100644 (file)
@@ -75,4 +75,46 @@ int mt76_wcid_alloc(unsigned long *mask, int size)
 }
 EXPORT_SYMBOL_GPL(mt76_wcid_alloc);
 
+int mt76_get_min_avg_rssi(struct mt76_dev *dev)
+{
+       struct mt76_wcid *wcid;
+       int i, j, min_rssi = 0;
+       s8 cur_rssi;
+
+       local_bh_disable();
+       rcu_read_lock();
+
+       for (i = 0; i < ARRAY_SIZE(dev->wcid_mask); i++) {
+               unsigned long mask = dev->wcid_mask[i];
+
+               if (!mask)
+                       continue;
+
+               for (j = i * BITS_PER_LONG; mask; j++, mask >>= 1) {
+                       if (!(mask & 1))
+                               continue;
+
+                       wcid = rcu_dereference(dev->wcid[j]);
+                       if (!wcid)
+                               continue;
+
+                       spin_lock(&dev->rx_lock);
+                       if (wcid->inactive_count++ < 5)
+                               cur_rssi = -ewma_signal_read(&wcid->rssi);
+                       else
+                               cur_rssi = 0;
+                       spin_unlock(&dev->rx_lock);
+
+                       if (cur_rssi < min_rssi)
+                               min_rssi = cur_rssi;
+               }
+       }
+
+       rcu_read_unlock();
+       local_bh_enable();
+
+       return min_rssi;
+}
+EXPORT_SYMBOL_GPL(mt76_get_min_avg_rssi);
+
 MODULE_LICENSE("Dual BSD/GPL");
index 7f3e398..f7edeff 100644 (file)
@@ -124,9 +124,9 @@ static u16 mt7601u_rx_next_seg_len(u8 *data, u32 data_len)
        u16 dma_len = get_unaligned_le16(data);
 
        if (data_len < min_seg_len ||
-           WARN_ON(!dma_len) ||
-           WARN_ON(dma_len + MT_DMA_HDRS > data_len) ||
-           WARN_ON(dma_len & 0x3))
+           WARN_ON_ONCE(!dma_len) ||
+           WARN_ON_ONCE(dma_len + MT_DMA_HDRS > data_len) ||
+           WARN_ON_ONCE(dma_len & 0x3))
                return 0;
 
        return MT_DMA_HDRS + dma_len;
index 662d127..57b503a 100644 (file)
@@ -17,7 +17,7 @@
 
 struct mt7601u_dev;
 
-#define MT7601U_EE_MAX_VER                     0x0c
+#define MT7601U_EE_MAX_VER                     0x0d
 #define MT7601U_EEPROM_SIZE                    256
 
 #define MT7601U_DEFAULT_TX_POWER               6
index baebfbd..cea83d1 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
 #
 # Copyright (c) 2015-2016 Quantenna Communications, Inc.
 # All rights reserved.
index 528ca7f..14b569b 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015 Quantenna Communications
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015 Quantenna Communications. All rights reserved. */
 
 #ifndef QTNFMAC_BUS_H
 #define QTNFMAC_BUS_H
@@ -135,7 +122,5 @@ static __always_inline void qtnf_bus_unlock(struct qtnf_bus *bus)
 
 int qtnf_core_attach(struct qtnf_bus *bus);
 void qtnf_core_detach(struct qtnf_bus *bus);
-void qtnf_txflowblock(struct device *dev, bool state);
-void qtnf_txcomplete(struct device *dev, struct sk_buff *txp, bool success);
 
 #endif /* QTNFMAC_BUS_H */
index 51b33ec..45f4cef 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2012-2012 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #include <linux/kernel.h>
 #include <linux/etherdevice.h>
@@ -122,7 +109,8 @@ qtnf_change_virtual_intf(struct wiphy *wiphy,
                         struct vif_params *params)
 {
        struct qtnf_vif *vif = qtnf_netdev_get_priv(dev);
-       u8 *mac_addr;
+       u8 *mac_addr = NULL;
+       int use4addr = 0;
        int ret;
 
        ret = qtnf_validate_iface_combinations(wiphy, vif, type);
@@ -132,14 +120,14 @@ qtnf_change_virtual_intf(struct wiphy *wiphy,
                return ret;
        }
 
-       if (params)
+       if (params) {
                mac_addr = params->macaddr;
-       else
-               mac_addr = NULL;
+               use4addr = params->use_4addr;
+       }
 
        qtnf_scan_done(vif->mac, true);
 
-       ret = qtnf_cmd_send_change_intf_type(vif, type, mac_addr);
+       ret = qtnf_cmd_send_change_intf_type(vif, type, use4addr, mac_addr);
        if (ret) {
                pr_err("VIF%u.%u: failed to change type to %d\n",
                       vif->mac->macid, vif->vifid, type);
@@ -190,6 +178,7 @@ static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy,
        struct qtnf_wmac *mac;
        struct qtnf_vif *vif;
        u8 *mac_addr = NULL;
+       int use4addr = 0;
        int ret;
 
        mac = wiphy_priv(wiphy);
@@ -225,10 +214,12 @@ static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy,
                return ERR_PTR(-ENOTSUPP);
        }
 
-       if (params)
+       if (params) {
                mac_addr = params->macaddr;
+               use4addr = params->use_4addr;
+       }
 
-       ret = qtnf_cmd_send_add_intf(vif, type, mac_addr);
+       ret = qtnf_cmd_send_add_intf(vif, type, use4addr, mac_addr);
        if (ret) {
                pr_err("VIF%u.%u: failed to add VIF %pM\n",
                       mac->macid, vif->vifid, mac_addr);
@@ -359,11 +350,6 @@ static int qtnf_set_wiphy_params(struct wiphy *wiphy, u32 changed)
                return -EFAULT;
        }
 
-       if (changed & (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT)) {
-               pr_err("MAC%u: can't modify retry params\n", mac->macid);
-               return -EOPNOTSUPP;
-       }
-
        ret = qtnf_cmd_send_update_phy_params(mac, changed);
        if (ret)
                pr_err("MAC%u: failed to update PHY params\n", mac->macid);
@@ -1107,7 +1093,8 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
        wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
                        WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
                        WIPHY_FLAG_AP_UAPSD |
-                       WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+                       WIPHY_FLAG_HAS_CHANNEL_SWITCH |
+                       WIPHY_FLAG_4ADDR_STATION;
        wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
        if (hw_info->hw_capab & QLINK_HW_CAPAB_DFS_OFFLOAD)
index b734251..c374857 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #ifndef _QTN_FMAC_CFG80211_H_
 #define _QTN_FMAC_CFG80211_H_
index 659e764..0f48f54 100644 (file)
@@ -1,17 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #include <linux/types.h>
 #include <linux/skbuff.h>
@@ -72,6 +60,8 @@ static int qtnf_cmd_resp_result_decode(enum qlink_cmd_result qcode)
                return -EADDRINUSE;
        case QLINK_CMD_RESULT_EADDRNOTAVAIL:
                return -EADDRNOTAVAIL;
+       case QLINK_CMD_RESULT_EBUSY:
+               return -EBUSY;
        default:
                return -EFAULT;
        }
@@ -97,14 +87,12 @@ static int qtnf_cmd_send_with_reply(struct qtnf_bus *bus,
        vif_id = cmd->vifid;
        cmd->mhdr.len = cpu_to_le16(cmd_skb->len);
 
-       pr_debug("VIF%u.%u cmd=0x%.4X\n", mac_id, vif_id,
-                le16_to_cpu(cmd->cmd_id));
+       pr_debug("VIF%u.%u cmd=0x%.4X\n", mac_id, vif_id, cmd_id);
 
        if (bus->fw_state != QTNF_FW_STATE_ACTIVE &&
-           le16_to_cpu(cmd->cmd_id) != QLINK_CMD_FW_INIT) {
+           cmd_id != QLINK_CMD_FW_INIT) {
                pr_warn("VIF%u.%u: drop cmd 0x%.4X in fw state %d\n",
-                       mac_id, vif_id, le16_to_cpu(cmd->cmd_id),
-                       bus->fw_state);
+                       mac_id, vif_id, cmd_id, bus->fw_state);
                dev_kfree_skb(cmd_skb);
                return -ENODEV;
        }
@@ -138,7 +126,7 @@ out:
                return qtnf_cmd_resp_result_decode(le16_to_cpu(resp->result));
 
        pr_warn("VIF%u.%u: cmd 0x%.4X failed: %d\n",
-               mac_id, vif_id, le16_to_cpu(cmd->cmd_id), ret);
+               mac_id, vif_id, cmd_id, ret);
 
        return ret;
 }
@@ -732,6 +720,7 @@ out:
 
 static int qtnf_cmd_send_add_change_intf(struct qtnf_vif *vif,
                                         enum nl80211_iftype iftype,
+                                        int use4addr,
                                         u8 *mac_addr,
                                         enum qlink_cmd_type cmd_type)
 {
@@ -749,6 +738,7 @@ static int qtnf_cmd_send_add_change_intf(struct qtnf_vif *vif,
        qtnf_bus_lock(vif->mac->bus);
 
        cmd = (struct qlink_cmd_manage_intf *)cmd_skb->data;
+       cmd->intf_info.use4addr = use4addr;
 
        switch (iftype) {
        case NL80211_IFTYPE_AP:
@@ -784,17 +774,19 @@ out:
        return ret;
 }
 
-int qtnf_cmd_send_add_intf(struct qtnf_vif *vif,
-                          enum nl80211_iftype iftype, u8 *mac_addr)
+int qtnf_cmd_send_add_intf(struct qtnf_vif *vif, enum nl80211_iftype iftype,
+                          int use4addr, u8 *mac_addr)
 {
-       return qtnf_cmd_send_add_change_intf(vif, iftype, mac_addr,
+       return qtnf_cmd_send_add_change_intf(vif, iftype, use4addr, mac_addr,
                        QLINK_CMD_ADD_INTF);
 }
 
 int qtnf_cmd_send_change_intf_type(struct qtnf_vif *vif,
-                                  enum nl80211_iftype iftype, u8 *mac_addr)
+                                  enum nl80211_iftype iftype,
+                                  int use4addr,
+                                  u8 *mac_addr)
 {
-       return qtnf_cmd_send_add_change_intf(vif, iftype, mac_addr,
+       return qtnf_cmd_send_add_change_intf(vif, iftype, use4addr, mac_addr,
                                             QLINK_CMD_CHANGE_INTF);
 }
 
@@ -914,9 +906,8 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
        if (WARN_ON(resp->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
                return -E2BIG;
 
-       hwinfo->rd = kzalloc(sizeof(*hwinfo->rd)
-                            + sizeof(struct ieee80211_reg_rule)
-                            * resp->n_reg_rules, GFP_KERNEL);
+       hwinfo->rd = kzalloc(struct_size(hwinfo->rd, reg_rules,
+                                        resp->n_reg_rules), GFP_KERNEL);
 
        if (!hwinfo->rd)
                return -ENOMEM;
@@ -1558,11 +1549,11 @@ static int qtnf_cmd_resp_proc_phy_params(struct qtnf_wmac *mac,
                switch (tlv_type) {
                case QTN_TLV_ID_FRAG_THRESH:
                        phy_thr = (void *)tlv;
-                       mac_info->frag_thr = (u32)le16_to_cpu(phy_thr->thr);
+                       mac_info->frag_thr = le32_to_cpu(phy_thr->thr);
                        break;
                case QTN_TLV_ID_RTS_THRESH:
                        phy_thr = (void *)tlv;
-                       mac_info->rts_thr = (u32)le16_to_cpu(phy_thr->thr);
+                       mac_info->rts_thr = le32_to_cpu(phy_thr->thr);
                        break;
                case QTN_TLV_ID_SRETRY_LIMIT:
                        limit = (void *)tlv;
@@ -1810,15 +1801,23 @@ int qtnf_cmd_send_update_phy_params(struct qtnf_wmac *mac, u32 changed)
        qtnf_bus_lock(mac->bus);
 
        if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
-               qtnf_cmd_skb_put_tlv_u16(cmd_skb, QTN_TLV_ID_FRAG_THRESH,
+               qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_FRAG_THRESH,
                                         wiphy->frag_threshold);
        if (changed & WIPHY_PARAM_RTS_THRESHOLD)
-               qtnf_cmd_skb_put_tlv_u16(cmd_skb, QTN_TLV_ID_RTS_THRESH,
+               qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_RTS_THRESH,
                                         wiphy->rts_threshold);
        if (changed & WIPHY_PARAM_COVERAGE_CLASS)
                qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_COVERAGE_CLASS,
                                        wiphy->coverage_class);
 
+       if (changed & WIPHY_PARAM_RETRY_LONG)
+               qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_LRETRY_LIMIT,
+                                       wiphy->retry_long);
+
+       if (changed & WIPHY_PARAM_RETRY_SHORT)
+               qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_SRETRY_LIMIT,
+                                       wiphy->retry_short);
+
        ret = qtnf_cmd_send(mac->bus, cmd_skb);
        if (ret)
                goto out;
index 1ac4115..96dff64 100644 (file)
@@ -1,17 +1,5 @@
-/*
- * Copyright (c) 2016 Quantenna Communications, 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2016 Quantenna Communications. All rights reserved. */
 
 #ifndef QLINK_COMMANDS_H_
 #define QLINK_COMMANDS_H_
@@ -26,9 +14,11 @@ void qtnf_cmd_send_deinit_fw(struct qtnf_bus *bus);
 int qtnf_cmd_get_hw_info(struct qtnf_bus *bus);
 int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac);
 int qtnf_cmd_send_add_intf(struct qtnf_vif *vif, enum nl80211_iftype iftype,
-                          u8 *mac_addr);
+                          int use4addr, u8 *mac_addr);
 int qtnf_cmd_send_change_intf_type(struct qtnf_vif *vif,
-                                  enum nl80211_iftype iftype, u8 *mac_addr);
+                                  enum nl80211_iftype iftype,
+                                  int use4addr,
+                                  u8 *mac_addr);
 int qtnf_cmd_send_del_intf(struct qtnf_vif *vif);
 int qtnf_cmd_band_info_get(struct qtnf_wmac *mac,
                           struct ieee80211_supported_band *band);
index 5d18a4a..ee1b75f 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -195,6 +182,7 @@ static int qtnf_netdev_set_mac_address(struct net_device *ndev, void *addr)
        qtnf_scan_done(vif->mac, true);
 
        ret = qtnf_cmd_send_change_intf_type(vif, vif->wdev.iftype,
+                                            vif->wdev.use_4addr,
                                             sa->sa_data);
 
        if (ret)
@@ -545,7 +533,8 @@ static int qtnf_core_mac_attach(struct qtnf_bus *bus, unsigned int macid)
                goto error;
        }
 
-       ret = qtnf_cmd_send_add_intf(vif, vif->wdev.iftype, vif->mac_addr);
+       ret = qtnf_cmd_send_add_intf(vif, vif->wdev.iftype,
+                                    vif->wdev.use_4addr, vif->mac_addr);
        if (ret) {
                pr_err("MAC%u: failed to add VIF\n", macid);
                goto error;
index 2930550..a31cff4 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #ifndef _QTN_FMAC_CORE_H_
 #define _QTN_FMAC_CORE_H_
index 9f826b9..598ece7 100644 (file)
@@ -1,32 +1,11 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #include "debug.h"
 
-#undef pr_fmt
-#define pr_fmt(fmt)    "qtnfmac dbg: %s: " fmt, __func__
-
 void qtnf_debugfs_init(struct qtnf_bus *bus, const char *name)
 {
        bus->dbg_dir = debugfs_create_dir(name, NULL);
-
-       if (IS_ERR_OR_NULL(bus->dbg_dir)) {
-               pr_warn("failed to create debugfs root dir\n");
-               bus->dbg_dir = NULL;
-       }
 }
 
 void qtnf_debugfs_remove(struct qtnf_bus *bus)
@@ -38,9 +17,5 @@ void qtnf_debugfs_remove(struct qtnf_bus *bus)
 void qtnf_debugfs_add_entry(struct qtnf_bus *bus, const char *name,
                            int (*fn)(struct seq_file *seq, void *data))
 {
-       struct dentry *entry;
-
-       entry = debugfs_create_devm_seqfile(bus->dev, name, bus->dbg_dir, fn);
-       if (IS_ERR_OR_NULL(entry))
-               pr_warn("failed to add entry (%s)\n", name);
+       debugfs_create_devm_seqfile(bus->dev, name, bus->dbg_dir, fn);
 }
index d6dd12b..61b4553 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #ifndef _QTN_FMAC_DEBUG_H_
 #define _QTN_FMAC_DEBUG_H_
index 8b542b4..3fd1a92 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -158,6 +145,12 @@ qtnf_event_handle_bss_join(struct qtnf_vif *vif,
                           const struct qlink_event_bss_join *join_info,
                           u16 len)
 {
+       struct wiphy *wiphy = priv_to_wiphy(vif->mac);
+       enum ieee80211_statuscode status = le16_to_cpu(join_info->status);
+       struct cfg80211_chan_def chandef;
+       struct cfg80211_bss *bss = NULL;
+       u8 *ie = NULL;
+
        if (unlikely(len < sizeof(*join_info))) {
                pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
                       vif->mac->macid, vif->vifid, len,
@@ -171,15 +164,80 @@ qtnf_event_handle_bss_join(struct qtnf_vif *vif,
                return -EPROTO;
        }
 
-       pr_debug("VIF%u.%u: BSSID:%pM\n", vif->mac->macid, vif->vifid,
-                join_info->bssid);
+       pr_debug("VIF%u.%u: BSSID:%pM status:%u\n",
+                vif->mac->macid, vif->vifid, join_info->bssid, status);
+
+       if (status == WLAN_STATUS_SUCCESS) {
+               qlink_chandef_q2cfg(wiphy, &join_info->chan, &chandef);
+               if (!cfg80211_chandef_valid(&chandef)) {
+                       pr_warn("MAC%u.%u: bad channel freq=%u cf1=%u cf2=%u bw=%u\n",
+                               vif->mac->macid, vif->vifid,
+                               chandef.chan->center_freq,
+                               chandef.center_freq1,
+                               chandef.center_freq2,
+                               chandef.width);
+                       status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto done;
+               }
 
+               bss = cfg80211_get_bss(wiphy, chandef.chan, join_info->bssid,
+                                      NULL, 0, IEEE80211_BSS_TYPE_ESS,
+                                      IEEE80211_PRIVACY_ANY);
+               if (!bss) {
+                       pr_warn("VIF%u.%u: add missing BSS:%pM chan:%u\n",
+                               vif->mac->macid, vif->vifid,
+                               join_info->bssid, chandef.chan->hw_value);
+
+                       if (!vif->wdev.ssid_len) {
+                               pr_warn("VIF%u.%u: SSID unknown for BSS:%pM\n",
+                                       vif->mac->macid, vif->vifid,
+                                       join_info->bssid);
+                               status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                               goto done;
+                       }
+
+                       ie = kzalloc(2 + vif->wdev.ssid_len, GFP_KERNEL);
+                       if (!ie) {
+                               pr_warn("VIF%u.%u: IE alloc failed for BSS:%pM\n",
+                                       vif->mac->macid, vif->vifid,
+                                       join_info->bssid);
+                               status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                               goto done;
+                       }
+
+                       ie[0] = WLAN_EID_SSID;
+                       ie[1] = vif->wdev.ssid_len;
+                       memcpy(ie + 2, vif->wdev.ssid, vif->wdev.ssid_len);
+
+                       bss = cfg80211_inform_bss(wiphy, chandef.chan,
+                                                 CFG80211_BSS_FTYPE_UNKNOWN,
+                                                 join_info->bssid, 0,
+                                                 WLAN_CAPABILITY_ESS, 100,
+                                                 ie, 2 + vif->wdev.ssid_len,
+                                                 0, GFP_KERNEL);
+                       if (!bss) {
+                               pr_warn("VIF%u.%u: can't connect to unknown BSS: %pM\n",
+                                       vif->mac->macid, vif->vifid,
+                                       join_info->bssid);
+                               status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                               goto done;
+                       }
+               }
+       }
+
+done:
        cfg80211_connect_result(vif->netdev, join_info->bssid, NULL, 0, NULL,
-                               0, le16_to_cpu(join_info->status), GFP_KERNEL);
+                               0, status, GFP_KERNEL);
+       if (bss) {
+               if (!ether_addr_equal(vif->bssid, join_info->bssid))
+                       ether_addr_copy(vif->bssid, join_info->bssid);
+               cfg80211_put_bss(wiphy, bss);
+       }
 
-       if (le16_to_cpu(join_info->status) == WLAN_STATUS_SUCCESS)
+       if (status == WLAN_STATUS_SUCCESS)
                netif_carrier_on(vif->netdev);
 
+       kfree(ie);
        return 0;
 }
 
index ae759b6..533ad99 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #ifndef _QTN_FMAC_EVENT_H_
 #define _QTN_FMAC_EVENT_H_
index 598edb8..cbcda57 100644 (file)
@@ -559,6 +559,9 @@ static irqreturn_t qtnf_pcie_topaz_interrupt(int irq, void *data)
        if (!priv->msi_enabled && !qtnf_topaz_intx_asserted(ts))
                return IRQ_NONE;
 
+       if (!priv->msi_enabled)
+               qtnf_deassert_intx(ts);
+
        priv->pcie_irq_count++;
 
        qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_in);
@@ -571,9 +574,6 @@ static irqreturn_t qtnf_pcie_topaz_interrupt(int irq, void *data)
 
        tasklet_hi_schedule(&priv->reclaim_tq);
 
-       if (!priv->msi_enabled)
-               qtnf_deassert_intx(ts);
-
        return IRQ_HANDLED;
 }
 
index 8d62add..27fdb5b 100644 (file)
@@ -1,25 +1,12 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #ifndef _QTN_QLINK_H_
 #define _QTN_QLINK_H_
 
 #include <linux/ieee80211.h>
 
-#define QLINK_PROTO_VER                11
+#define QLINK_PROTO_VER                13
 
 #define QLINK_MACID_RSVD               0xFF
 #define QLINK_VIFID_RSVD               0xFF
@@ -105,7 +92,8 @@ struct qlink_intf_info {
        __le16 if_type;
        __le16 vlanid;
        u8 mac_addr[ETH_ALEN];
-       u8 rsvd[2];
+       u8 use4addr;
+       u8 rsvd[1];
 } __packed;
 
 enum qlink_sta_flags {
@@ -733,6 +721,7 @@ enum qlink_cmd_result {
        QLINK_CMD_RESULT_EALREADY,
        QLINK_CMD_RESULT_EADDRINUSE,
        QLINK_CMD_RESULT_EADDRNOTAVAIL,
+       QLINK_CMD_RESULT_EBUSY,
 };
 
 /**
@@ -986,11 +975,13 @@ struct qlink_event_sta_deauth {
 /**
  * struct qlink_event_bss_join - data for QLINK_EVENT_BSS_JOIN event
  *
+ * @chan: new operating channel definition
  * @bssid: BSSID of a BSS which interface tried to joined.
  * @status: status of joining attempt, see &enum ieee80211_statuscode.
  */
 struct qlink_event_bss_join {
        struct qlink_event ehdr;
+       struct qlink_chandef chan;
        u8 bssid[ETH_ALEN];
        __le16 status;
 } __packed;
@@ -1182,7 +1173,7 @@ struct qlink_iface_limit_record {
 
 struct qlink_tlv_frag_rts_thr {
        struct qlink_tlv_hdr hdr;
-       __le16 thr;
+       __le32 thr;
 } __packed;
 
 struct qlink_tlv_rlimit {
index aeeda81..72bfd17 100644 (file)
@@ -1,17 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #include <linux/nl80211.h>
 
index 960d5d9..781ea7f 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #ifndef _QTN_FMAC_QLINK_UTIL_H_
 #define _QTN_FMAC_QLINK_UTIL_H_
@@ -69,6 +56,17 @@ static inline void qtnf_cmd_skb_put_tlv_u16(struct sk_buff *skb,
        memcpy(hdr->val, &tmp, sizeof(tmp));
 }
 
+static inline void qtnf_cmd_skb_put_tlv_u32(struct sk_buff *skb,
+                                           u16 tlv_id, u32 value)
+{
+       struct qlink_tlv_hdr *hdr = skb_put(skb, sizeof(*hdr) + sizeof(value));
+       __le32 tmp = cpu_to_le32(value);
+
+       hdr->type = cpu_to_le16(tlv_id);
+       hdr->len = cpu_to_le16(sizeof(value));
+       memcpy(hdr->val, &tmp, sizeof(tmp));
+}
+
 u16 qlink_iface_type_to_nl_mask(u16 qlink_type);
 u8 qlink_chan_width_mask_to_nl(u16 qlink_mask);
 void qlink_chandef_q2cfg(struct wiphy *wiphy,
index 40295a5..82d8799 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #ifndef        _QTN_HW_IDS_H_
 #define        _QTN_HW_IDS_H_
index 2ec3341..ff67895 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #include <linux/types.h>
 #include <linux/io.h>
index c2a3702..52cac54 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #ifndef _QTN_FMAC_SHM_IPC_H_
 #define _QTN_FMAC_SHM_IPC_H_
index 95a5f89..78be70d 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #ifndef _QTN_FMAC_SHM_IPC_DEFS_H_
 #define _QTN_FMAC_SHM_IPC_DEFS_H_
index 345f34e..95356e2 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #include <linux/types.h>
 #include <linux/export.h>
index 9a473e0..c0b76f8 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #ifndef _QTN_FMAC_TRANS_H_
 #define _QTN_FMAC_TRANS_H_
index 3bc96b2..cda6f5f 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015-2016 Quantenna Communications, Inc.
- * All rights reserved.
- *
- * 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 the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
 
 #include "util.h"
 #include "qtn_hw_ids.h"
index b8744ba..a14b707 100644 (file)
@@ -1,18 +1,5 @@
-/*
- * Copyright (c) 2015 Quantenna Communications
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2015 Quantenna Communications. All rights reserved. */
 
 #ifndef QTNFMAC_UTIL_H
 #define QTNFMAC_UTIL_H
index 0e95555..7f813f6 100644 (file)
@@ -5477,7 +5477,7 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
                rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
                rt2800_register_write(rt2x00dev, MIMO_PS_CFG, 0x00000002);
                rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0x00150F0F);
-               rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x06060606);
+               rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x00000000);
                rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0);
                rt2800_register_write(rt2x00dev, TX1_BB_GAIN_ATTEN, 0x0);
                rt2800_register_write(rt2x00dev, TX0_RF_GAIN_ATTEN, 0x6C6C666C);
index 61ba573..05a2e8d 100644 (file)
@@ -656,36 +656,24 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
        intf->driver_folder =
            debugfs_create_dir(intf->rt2x00dev->ops->name,
                               rt2x00dev->hw->wiphy->debugfsdir);
-       if (IS_ERR(intf->driver_folder) || !intf->driver_folder)
-               goto exit;
 
        intf->driver_entry =
            rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob);
-       if (IS_ERR(intf->driver_entry) || !intf->driver_entry)
-               goto exit;
 
        intf->chipset_entry =
            rt2x00debug_create_file_chipset("chipset",
                                            intf, &intf->chipset_blob);
-       if (IS_ERR(intf->chipset_entry) || !intf->chipset_entry)
-               goto exit;
 
        intf->dev_flags = debugfs_create_file("dev_flags", 0400,
                                              intf->driver_folder, intf,
                                              &rt2x00debug_fop_dev_flags);
-       if (IS_ERR(intf->dev_flags) || !intf->dev_flags)
-               goto exit;
 
        intf->cap_flags = debugfs_create_file("cap_flags", 0400,
                                              intf->driver_folder, intf,
                                              &rt2x00debug_fop_cap_flags);
-       if (IS_ERR(intf->cap_flags) || !intf->cap_flags)
-               goto exit;
 
        intf->register_folder =
            debugfs_create_dir("register", intf->driver_folder);
-       if (IS_ERR(intf->register_folder) || !intf->register_folder)
-               goto exit;
 
 #define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name)            \
 ({                                                                     \
@@ -695,9 +683,6 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
                                           0600,                        \
                                           (__intf)->register_folder,   \
                                           &(__intf)->offset_##__name); \
-               if (IS_ERR((__intf)->__name##_off_entry) ||             \
-                   !(__intf)->__name##_off_entry)                      \
-                       goto exit;                                      \
                                                                        \
                (__intf)->__name##_val_entry =                          \
                        debugfs_create_file(__stringify(__name) "_value", \
@@ -705,9 +690,6 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
                                            (__intf)->register_folder,  \
                                            (__intf),                   \
                                            &rt2x00debug_fop_##__name); \
-               if (IS_ERR((__intf)->__name##_val_entry) ||             \
-                   !(__intf)->__name##_val_entry)                      \
-                       goto exit;                                      \
        }                                                               \
 })
 
@@ -721,15 +703,10 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
 
        intf->queue_folder =
            debugfs_create_dir("queue", intf->driver_folder);
-       if (IS_ERR(intf->queue_folder) || !intf->queue_folder)
-               goto exit;
 
        intf->queue_frame_dump_entry =
                debugfs_create_file("dump", 0400, intf->queue_folder,
                                    intf, &rt2x00debug_fop_queue_dump);
-       if (IS_ERR(intf->queue_frame_dump_entry)
-               || !intf->queue_frame_dump_entry)
-               goto exit;
 
        skb_queue_head_init(&intf->frame_dump_skbqueue);
        init_waitqueue_head(&intf->frame_dump_waitqueue);
@@ -747,10 +724,6 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
 #endif
 
        return;
-
-exit:
-       rt2x00debug_deregister(rt2x00dev);
-       rt2x00_err(rt2x00dev, "Failed to register debug handler\n");
 }
 
 void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev)
index 4c5de8f..52b9fc4 100644 (file)
@@ -321,97 +321,12 @@ static int rt61pci_config_shared_key(struct rt2x00_dev *rt2x00dev,
                                     struct rt2x00lib_crypto *crypto,
                                     struct ieee80211_key_conf *key)
 {
-       struct hw_key_entry key_entry;
-       struct rt2x00_field32 field;
-       u32 mask;
-       u32 reg;
-
-       if (crypto->cmd == SET_KEY) {
-               /*
-                * rt2x00lib can't determine the correct free
-                * key_idx for shared keys. We have 1 register
-                * with key valid bits. The goal is simple, read
-                * the register, if that is full we have no slots
-                * left.
-                * Note that each BSS is allowed to have up to 4
-                * shared keys, so put a mask over the allowed
-                * entries.
-                */
-               mask = (0xf << crypto->bssidx);
-
-               reg = rt2x00mmio_register_read(rt2x00dev, SEC_CSR0);
-               reg &= mask;
-
-               if (reg && reg == mask)
-                       return -ENOSPC;
-
-               key->hw_key_idx += reg ? ffz(reg) : 0;
-
-               /*
-                * Upload key to hardware
-                */
-               memcpy(key_entry.key, crypto->key,
-                      sizeof(key_entry.key));
-               memcpy(key_entry.tx_mic, crypto->tx_mic,
-                      sizeof(key_entry.tx_mic));
-               memcpy(key_entry.rx_mic, crypto->rx_mic,
-                      sizeof(key_entry.rx_mic));
-
-               reg = SHARED_KEY_ENTRY(key->hw_key_idx);
-               rt2x00mmio_register_multiwrite(rt2x00dev, reg,
-                                              &key_entry, sizeof(key_entry));
-
-               /*
-                * The cipher types are stored over 2 registers.
-                * bssidx 0 and 1 keys are stored in SEC_CSR1 and
-                * bssidx 1 and 2 keys are stored in SEC_CSR5.
-                * Using the correct defines correctly will cause overhead,
-                * so just calculate the correct offset.
-                */
-               if (key->hw_key_idx < 8) {
-                       field.bit_offset = (3 * key->hw_key_idx);
-                       field.bit_mask = 0x7 << field.bit_offset;
-
-                       reg = rt2x00mmio_register_read(rt2x00dev, SEC_CSR1);
-                       rt2x00_set_field32(&reg, field, crypto->cipher);
-                       rt2x00mmio_register_write(rt2x00dev, SEC_CSR1, reg);
-               } else {
-                       field.bit_offset = (3 * (key->hw_key_idx - 8));
-                       field.bit_mask = 0x7 << field.bit_offset;
-
-                       reg = rt2x00mmio_register_read(rt2x00dev, SEC_CSR5);
-                       rt2x00_set_field32(&reg, field, crypto->cipher);
-                       rt2x00mmio_register_write(rt2x00dev, SEC_CSR5, reg);
-               }
-
-               /*
-                * The driver does not support the IV/EIV generation
-                * in hardware. However it doesn't support the IV/EIV
-                * inside the ieee80211 frame either, but requires it
-                * to be provided separately for the descriptor.
-                * rt2x00lib will cut the IV/EIV data out of all frames
-                * given to us by mac80211, but we must tell mac80211
-                * to generate the IV/EIV data.
-                */
-               key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
-       }
-
        /*
-        * SEC_CSR0 contains only single-bit fields to indicate
-        * a particular key is valid. Because using the FIELD32()
-        * defines directly will cause a lot of overhead, we use
-        * a calculation to determine the correct bit directly.
+        * Let the software handle the shared keys,
+        * since the hardware decryption does not work reliably,
+        * because the firmware does not know the key's keyidx.
         */
-       mask = 1 << key->hw_key_idx;
-
-       reg = rt2x00mmio_register_read(rt2x00dev, SEC_CSR0);
-       if (crypto->cmd == SET_KEY)
-               reg |= mask;
-       else if (crypto->cmd == DISABLE_KEY)
-               reg &= ~mask;
-       rt2x00mmio_register_write(rt2x00dev, SEC_CSR0, reg);
-
-       return 0;
+       return -EOPNOTSUPP;
 }
 
 static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
index 33ad875..44a943d 100644 (file)
@@ -959,7 +959,7 @@ static int translate_frame(ray_dev_t *local, struct tx_msg __iomem *ptx,
                if (proto == htons(ETH_P_AARP) || proto == htons(ETH_P_IPX)) {
                        /* This is the selective translation table, only 2 entries */
                        writeb(0xf8,
-                              &((struct snaphdr_t __iomem *)ptx->var)->org[3]);
+                              &((struct snaphdr_t __iomem *)ptx->var)->org[2]);
                }
                /* Copy body of ethernet packet without ethernet header */
                memcpy_toio((void __iomem *)&ptx->var +
@@ -2211,7 +2211,7 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs,
                        untranslate(local, skb, total_len);
                }
        } else { /* sniffer mode, so just pass whole packet */
-       };
+       }
 
 /************************/
        /* Now pick up the rest of the fragments if any */
index 2966681..5d6b06d 100644 (file)
@@ -2,4 +2,4 @@ rtl818x_pci-objs        := dev.o rtl8225.o sa2400.o max2820.o grf5101.o rtl8225se.o
 
 obj-$(CONFIG_RTL8180)  += rtl818x_pci.o
 
-ccflags-y += -Idrivers/net/wireless/realtek/rtl818x
+ccflags-y += -I $(srctree)/$(src)/..
index 225c1c8..e2b1bfb 100644 (file)
@@ -803,7 +803,7 @@ static void rtl8180_config_cardbus(struct ieee80211_hw *dev)
                rtl818x_iowrite16(priv, FEMR_SE, 0xffff);
        } else {
                reg16 = rtl818x_ioread16(priv, &priv->map->FEMR);
-                       reg16 |= (1 << 15) | (1 << 14) | (1 << 4);
+               reg16 |= (1 << 15) | (1 << 14) | (1 << 4);
                rtl818x_iowrite16(priv, &priv->map->FEMR, reg16);
        }
 
index ff07491..95bac73 100644 (file)
@@ -2,4 +2,4 @@ rtl8187-objs            := dev.o rtl8225.o leds.o rfkill.o
 
 obj-$(CONFIG_RTL8187)  += rtl8187.o
 
-ccflags-y += -Idrivers/net/wireless/realtek/rtl818x
+ccflags-y += -I $(srctree)/$(src)/..
index ef9b502..7aa68fe 100644 (file)
@@ -2172,8 +2172,6 @@ label_lps_done:
                ;
        }
 
-       rtlpriv->link_info.num_rx_inperiod = 0;
-       rtlpriv->link_info.num_tx_inperiod = 0;
        for (tid = 0; tid <= 7; tid++)
                rtlpriv->link_info.tidtx_inperiod[tid] = 0;
 
@@ -2236,6 +2234,8 @@ label_lps_done:
                        rtlpriv->btcoexist.btc_info.in_4way = false;
        }
 
+       rtlpriv->link_info.num_rx_inperiod = 0;
+       rtlpriv->link_info.num_tx_inperiod = 0;
        rtlpriv->link_info.bcn_rx_inperiod = 0;
 
        /* <6> scan list */
index 4bf7967..ce23339 100644 (file)
@@ -1957,5 +1957,7 @@ void rtl_dm_diginit(struct ieee80211_hw *hw, u32 cur_igvalue)
        dm_digtable->bt30_cur_igi = 0x32;
        dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX;
        dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_LOWRSSI;
+       dm_digtable->pre_cck_fa_state = 0;
+       dm_digtable->cur_cck_fa_state = 0;
 }
 EXPORT_SYMBOL(rtl_dm_diginit);
index d70385b..8186650 100644 (file)
@@ -463,12 +463,9 @@ static const struct file_operations file_ops_common_write = {
 #define RTL_DEBUGFS_ADD_CORE(name, mode, fopname)                         \
        do {                                                               \
                rtl_debug_priv_ ##name.rtlpriv = rtlpriv;                  \
-               if (!debugfs_create_file(#name, mode,                      \
-                                        parent, &rtl_debug_priv_ ##name,  \
-                                        &file_ops_ ##fopname))            \
-                       pr_err("Unable to initialize debugfs:%s/%s\n",     \
-                              rtlpriv->dbg.debugfs_name,                  \
-                              #name);                                     \
+               debugfs_create_file(#name, mode, parent,                   \
+                                   &rtl_debug_priv_ ##name,               \
+                                   &file_ops_ ##fopname);                 \
        } while (0)
 
 #define RTL_DEBUGFS_ADD(name)                                             \
@@ -486,11 +483,6 @@ void rtl_debug_add_one(struct ieee80211_hw *hw)
 
        rtlpriv->dbg.debugfs_dir =
                debugfs_create_dir(rtlpriv->dbg.debugfs_name, debugfs_topdir);
-       if (!rtlpriv->dbg.debugfs_dir) {
-               pr_err("Unable to init debugfs:/%s/%s\n", rtlpriv->cfg->name,
-                      rtlpriv->dbg.debugfs_name);
-               return;
-       }
 
        parent = rtlpriv->dbg.debugfs_dir;
 
index 42a6fba..acfd54c 100644 (file)
@@ -151,8 +151,14 @@ static u8 rtl8723e_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct dig_t *dm_digtable = &rtlpriv->dm_digtable;
+       struct rtl_mac *mac = rtl_mac(rtlpriv);
        long rssi_val_min = 0;
 
+       if (mac->link_state == MAC80211_LINKED &&
+           mac->opmode == NL80211_IFTYPE_STATION &&
+           rtlpriv->link_info.bcn_rx_inperiod == 0)
+               return 0;
+
        if ((dm_digtable->curmultista_cstate == DIG_MULTISTA_CONNECT) &&
            (dm_digtable->cursta_cstate == DIG_STA_CONNECT)) {
                if (rtlpriv->dm.entry_min_undec_sm_pwdb != 0)
@@ -417,6 +423,8 @@ static void rtl8723e_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw)
                } else {
                        rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0xcd);
                        rtl_set_bbreg(hw, RCCK0_SYSTEM, MASKBYTE1, 0x47);
+                       dm_digtable->pre_cck_fa_state = 0;
+                       dm_digtable->cur_cck_fa_state = 0;
 
                }
                dm_digtable->pre_cck_pd_state = dm_digtable->cur_cck_pd_state;
@@ -665,7 +673,7 @@ void rtl8723e_dm_check_txpower_tracking(struct ieee80211_hw *hw)
 void rtl8723e_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rate_adaptive *p_ra = &(rtlpriv->ra);
+       struct rate_adaptive *p_ra = &rtlpriv->ra;
 
        p_ra->ratr_state = DM_RATR_STA_INIT;
        p_ra->pre_ratr_state = DM_RATR_STA_INIT;
@@ -677,6 +685,89 @@ void rtl8723e_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw)
 
 }
 
+void rtl8723e_dm_refresh_rate_adaptive_mask(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+       struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
+       struct rate_adaptive *p_ra = &rtlpriv->ra;
+       u32 low_rssithresh_for_ra, high_rssithresh_for_ra;
+       struct ieee80211_sta *sta = NULL;
+
+       if (is_hal_stop(rtlhal)) {
+               RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                        " driver is going to unload\n");
+               return;
+       }
+
+       if (!rtlpriv->dm.useramask) {
+               RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                        " driver does not control rate adaptive mask\n");
+               return;
+       }
+
+       if (mac->link_state == MAC80211_LINKED &&
+           mac->opmode == NL80211_IFTYPE_STATION) {
+               switch (p_ra->pre_ratr_state) {
+               case DM_RATR_STA_HIGH:
+                       high_rssithresh_for_ra = 50;
+                       low_rssithresh_for_ra = 20;
+                       break;
+               case DM_RATR_STA_MIDDLE:
+                       high_rssithresh_for_ra = 55;
+                       low_rssithresh_for_ra = 20;
+                       break;
+               case DM_RATR_STA_LOW:
+                       high_rssithresh_for_ra = 60;
+                       low_rssithresh_for_ra = 25;
+                       break;
+               default:
+                       high_rssithresh_for_ra = 50;
+                       low_rssithresh_for_ra = 20;
+                       break;
+               }
+
+               if (rtlpriv->link_info.bcn_rx_inperiod == 0)
+                       switch (p_ra->pre_ratr_state) {
+                       case DM_RATR_STA_HIGH:
+                       default:
+                               p_ra->ratr_state = DM_RATR_STA_MIDDLE;
+                               break;
+                       case DM_RATR_STA_MIDDLE:
+                       case DM_RATR_STA_LOW:
+                               p_ra->ratr_state = DM_RATR_STA_LOW;
+                               break;
+                       }
+               else if (rtlpriv->dm.undec_sm_pwdb > high_rssithresh_for_ra)
+                       p_ra->ratr_state = DM_RATR_STA_HIGH;
+               else if (rtlpriv->dm.undec_sm_pwdb > low_rssithresh_for_ra)
+                       p_ra->ratr_state = DM_RATR_STA_MIDDLE;
+               else
+                       p_ra->ratr_state = DM_RATR_STA_LOW;
+
+               if (p_ra->pre_ratr_state != p_ra->ratr_state) {
+                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                                "RSSI = %ld\n",
+                                rtlpriv->dm.undec_sm_pwdb);
+                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                                "RSSI_LEVEL = %d\n", p_ra->ratr_state);
+                       RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD,
+                                "PreState = %d, CurState = %d\n",
+                                p_ra->pre_ratr_state, p_ra->ratr_state);
+
+                       rcu_read_lock();
+                       sta = rtl_find_sta(hw, mac->bssid);
+                       if (sta)
+                               rtlpriv->cfg->ops->update_rate_tbl(hw, sta,
+                                                          p_ra->ratr_state,
+                                                                     true);
+                       rcu_read_unlock();
+
+                       p_ra->pre_ratr_state = p_ra->ratr_state;
+               }
+       }
+}
+
 void rtl8723e_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -826,7 +917,7 @@ void rtl8723e_dm_watchdog(struct ieee80211_hw *hw)
                rtl8723e_dm_dynamic_bb_powersaving(hw);
                rtl8723e_dm_dynamic_txpower(hw);
                rtl8723e_dm_check_txpower_tracking(hw);
-               /* rtl92c_dm_refresh_rate_adaptive_mask(hw); */
+               rtl8723e_dm_refresh_rate_adaptive_mask(hw);
                rtl8723e_dm_bt_coexist(hw);
                rtl8723e_dm_check_edca_turbo(hw);
        }
index 07b8270..3103151 100644 (file)
@@ -266,8 +266,8 @@ static struct rtl_hal_ops rtl8723e_hal_ops = {
 static struct rtl_mod_params rtl8723e_mod_params = {
        .sw_crypto = false,
        .inactiveps = true,
-       .swctrl_lps = false,
-       .fwctrl_lps = true,
+       .swctrl_lps = true,
+       .fwctrl_lps = false,
        .aspm_support = 1,
        .debug_level = 0,
        .debug_mask = 0,
@@ -395,8 +395,8 @@ module_param_named(disable_watchdog, rtl8723e_mod_params.disable_watchdog,
                   bool, 0444);
 MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
 MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
-MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
-MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 1)\n");
+MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 0)\n");
 MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n");
 MODULE_PARM_DESC(aspm, "Set to 1 to enable ASPM (default 1)\n");
 MODULE_PARM_DESC(debug_level, "Set debug level (0-5) (default 0)");
index 1263b12..11f94b1 100644 (file)
@@ -332,7 +332,7 @@ static void _rtl8723be_phy_set_txpower_by_rate_base(struct ieee80211_hw *hw,
                                 "Invalid RateSection %d in Band 2.4G, Rf Path %d, %dTx in PHY_SetTxPowerByRateBase()\n",
                                 rate_section, path, txnum);
                        break;
-               };
+               }
        } else {
                RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
                         "Invalid Band %d in PHY_SetTxPowerByRateBase()\n",
@@ -374,7 +374,7 @@ static u8 _rtl8723be_phy_get_txpower_by_rate_base(struct ieee80211_hw *hw,
                                 "Invalid RateSection %d in Band 2.4G, Rf Path %d, %dTx in PHY_GetTxPowerByRateBase()\n",
                                 rate_section, path, txnum);
                        break;
-               };
+               }
        } else {
                RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
                         "Invalid Band %d in PHY_GetTxPowerByRateBase()\n",
@@ -694,7 +694,7 @@ static u8 _rtl8723be_get_rate_section_index(u32 regaddr)
                else if (regaddr >= 0xE20 && regaddr <= 0xE4C)
                        index = (u8)((regaddr - 0xE20) / 4);
                break;
-       };
+       }
        return index;
 }
 
index 8c6ca8e..d0c35f3 100644 (file)
@@ -297,11 +297,6 @@ int rsi_init_dbgfs(struct rsi_hw *adapter)
 
        dev_dbgfs->subdir = debugfs_create_dir(devdir, NULL);
 
-       if (!dev_dbgfs->subdir) {
-               kfree(dev_dbgfs);
-               return -ENOMEM;
-       }
-
        for (ii = 0; ii < adapter->num_debugfs_entries; ii++) {
                files = &dev_debugfs_files[ii];
                dev_dbgfs->rsi_files[ii] =
index 182b066..1dbaab2 100644 (file)
@@ -100,6 +100,9 @@ int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb)
        mgmt_desc->frame_type = TX_DOT11_MGMT;
        mgmt_desc->header_len = MIN_802_11_HDR_LEN;
        mgmt_desc->xtend_desc_size = header_size - FRAME_DESC_SZ;
+
+       if (ieee80211_is_probe_req(wh->frame_control))
+               mgmt_desc->frame_info = cpu_to_le16(RSI_INSERT_SEQ_IN_FW);
        mgmt_desc->frame_info |= cpu_to_le16(RATE_INFO_ENABLE);
        if (is_broadcast_ether_addr(wh->addr1))
                mgmt_desc->frame_info |= cpu_to_le16(RSI_BROADCAST_PKT);
index e56fc83..aded1ae 100644 (file)
@@ -229,6 +229,68 @@ static void rsi_register_rates_channels(struct rsi_hw *adapter, int band)
        /* sbands->ht_cap.mcs.rx_highest = 0x82; */
 }
 
+static int rsi_mac80211_hw_scan_start(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_scan_request *hw_req)
+{
+       struct cfg80211_scan_request *scan_req = &hw_req->req;
+       struct rsi_hw *adapter = hw->priv;
+       struct rsi_common *common = adapter->priv;
+       struct ieee80211_bss_conf *bss = &vif->bss_conf;
+
+       rsi_dbg(INFO_ZONE, "***** Hardware scan start *****\n");
+
+       if (common->fsm_state != FSM_MAC_INIT_DONE)
+               return -ENODEV;
+
+       if ((common->wow_flags & RSI_WOW_ENABLED) ||
+           scan_req->n_channels == 0)
+               return -EINVAL;
+
+       /* Scan already in progress. So return */
+       if (common->bgscan_en)
+               return -EBUSY;
+
+       /* If STA is not connected, return with special value 1, in order
+        * to start sw_scan in mac80211
+        */
+       if (!bss->assoc)
+               return 1;
+
+       mutex_lock(&common->mutex);
+       common->hwscan = scan_req;
+       if (!rsi_send_bgscan_params(common, RSI_START_BGSCAN)) {
+               if (!rsi_send_bgscan_probe_req(common, vif)) {
+                       rsi_dbg(INFO_ZONE, "Background scan started...\n");
+                       common->bgscan_en = true;
+               }
+       }
+       mutex_unlock(&common->mutex);
+
+       return 0;
+}
+
+static void rsi_mac80211_cancel_hw_scan(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif)
+{
+       struct rsi_hw *adapter = hw->priv;
+       struct rsi_common *common = adapter->priv;
+       struct cfg80211_scan_info info;
+
+       rsi_dbg(INFO_ZONE, "***** Hardware scan stop *****\n");
+       mutex_lock(&common->mutex);
+
+       if (common->bgscan_en) {
+               if (!rsi_send_bgscan_params(common, RSI_STOP_BGSCAN))
+                       common->bgscan_en = false;
+               info.aborted = false;
+               ieee80211_scan_completed(adapter->hw, &info);
+               rsi_dbg(INFO_ZONE, "Back ground scan cancelled\b\n");
+       }
+       common->hwscan = NULL;
+       mutex_unlock(&common->mutex);
+}
+
 /**
  * rsi_mac80211_detach() - This function is used to de-initialize the
  *                        Mac80211 stack.
@@ -1917,6 +1979,8 @@ static const struct ieee80211_ops mac80211_ops = {
        .suspend = rsi_mac80211_suspend,
        .resume  = rsi_mac80211_resume,
 #endif
+       .hw_scan = rsi_mac80211_hw_scan_start,
+       .cancel_hw_scan = rsi_mac80211_cancel_hw_scan,
 };
 
 /**
@@ -1999,6 +2063,9 @@ int rsi_mac80211_attach(struct rsi_common *common)
        common->max_stations = wiphy->max_ap_assoc_sta;
        rsi_dbg(ERR_ZONE, "Max Stations Allowed = %d\n", common->max_stations);
        hw->sta_data_size = sizeof(struct rsi_sta);
+
+       wiphy->max_scan_ssids = RSI_MAX_SCAN_SSIDS;
+       wiphy->max_scan_ie_len = RSI_MAX_SCAN_IE_LEN;
        wiphy->flags = WIPHY_FLAG_REPORTS_OBSS;
        wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
        wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
index 01d99ed..ca3a55e 100644 (file)
@@ -328,6 +328,7 @@ struct rsi_hw *rsi_91x_init(u16 oper_mode)
        }
 
        rsi_default_ps_params(adapter);
+       init_bgscan_params(common);
        spin_lock_init(&adapter->ps_lock);
        timer_setup(&common->roc_timer, rsi_roc_timeout, 0);
        init_completion(&common->wlan_init_completion);
index 1095df7..4042414 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <linux/etherdevice.h>
+#include <linux/timer.h>
 #include "rsi_mgmt.h"
 #include "rsi_common.h"
 #include "rsi_ps.h"
@@ -236,6 +237,18 @@ static void rsi_set_default_parameters(struct rsi_common *common)
        common->dtim_cnt = RSI_DTIM_COUNT;
 }
 
+void init_bgscan_params(struct rsi_common *common)
+{
+       memset((u8 *)&common->bgscan, 0, sizeof(struct rsi_bgscan_params));
+       common->bgscan.bgscan_threshold = RSI_DEF_BGSCAN_THRLD;
+       common->bgscan.roam_threshold = RSI_DEF_ROAM_THRLD;
+       common->bgscan.bgscan_periodicity = RSI_BGSCAN_PERIODICITY;
+       common->bgscan.num_bgscan_channels = 0;
+       common->bgscan.two_probe = 1;
+       common->bgscan.active_scan_duration = RSI_ACTIVE_SCAN_TIME;
+       common->bgscan.passive_scan_duration = RSI_PASSIVE_SCAN_TIME;
+}
+
 /**
  * rsi_set_contention_vals() - This function sets the contention values for the
  *                            backoff procedure.
@@ -1628,6 +1641,107 @@ int rsi_send_wowlan_request(struct rsi_common *common, u16 flags,
 }
 #endif
 
+int rsi_send_bgscan_params(struct rsi_common *common, int enable)
+{
+       struct rsi_bgscan_params *params = &common->bgscan;
+       struct cfg80211_scan_request *scan_req = common->hwscan;
+       struct rsi_bgscan_config *bgscan;
+       struct sk_buff *skb;
+       u16 frame_len = sizeof(*bgscan);
+       u8 i;
+
+       rsi_dbg(MGMT_TX_ZONE, "%s: Sending bgscan params frame\n", __func__);
+
+       skb = dev_alloc_skb(frame_len);
+       if (!skb)
+               return -ENOMEM;
+       memset(skb->data, 0, frame_len);
+
+       bgscan = (struct rsi_bgscan_config *)skb->data;
+       rsi_set_len_qno(&bgscan->desc_dword0.len_qno,
+                       (frame_len - FRAME_DESC_SZ), RSI_WIFI_MGMT_Q);
+       bgscan->desc_dword0.frame_type = BG_SCAN_PARAMS;
+       bgscan->bgscan_threshold = cpu_to_le16(params->bgscan_threshold);
+       bgscan->roam_threshold = cpu_to_le16(params->roam_threshold);
+       if (enable)
+               bgscan->bgscan_periodicity =
+                       cpu_to_le16(params->bgscan_periodicity);
+       bgscan->active_scan_duration =
+                       cpu_to_le16(params->active_scan_duration);
+       bgscan->passive_scan_duration =
+                       cpu_to_le16(params->passive_scan_duration);
+       bgscan->two_probe = params->two_probe;
+
+       bgscan->num_bgscan_channels = scan_req->n_channels;
+       for (i = 0; i < bgscan->num_bgscan_channels; i++)
+               bgscan->channels2scan[i] =
+                       cpu_to_le16(scan_req->channels[i]->hw_value);
+
+       skb_put(skb, frame_len);
+
+       return rsi_send_internal_mgmt_frame(common, skb);
+}
+
+/* This function sends the probe request to be used by firmware in
+ * background scan
+ */
+int rsi_send_bgscan_probe_req(struct rsi_common *common,
+                             struct ieee80211_vif *vif)
+{
+       struct cfg80211_scan_request *scan_req = common->hwscan;
+       struct rsi_bgscan_probe *bgscan;
+       struct sk_buff *skb;
+       struct sk_buff *probereq_skb;
+       u16 frame_len = sizeof(*bgscan);
+       size_t ssid_len = 0;
+       u8 *ssid = NULL;
+
+       rsi_dbg(MGMT_TX_ZONE,
+               "%s: Sending bgscan probe req frame\n", __func__);
+
+       if (common->priv->sc_nvifs <= 0)
+               return -ENODEV;
+
+       if (scan_req->n_ssids) {
+               ssid = scan_req->ssids[0].ssid;
+               ssid_len = scan_req->ssids[0].ssid_len;
+       }
+
+       skb = dev_alloc_skb(frame_len + MAX_BGSCAN_PROBE_REQ_LEN);
+       if (!skb)
+               return -ENOMEM;
+       memset(skb->data, 0, frame_len + MAX_BGSCAN_PROBE_REQ_LEN);
+
+       bgscan = (struct rsi_bgscan_probe *)skb->data;
+       bgscan->desc_dword0.frame_type = BG_SCAN_PROBE_REQ;
+       bgscan->flags = cpu_to_le16(HOST_BG_SCAN_TRIG);
+       if (common->band == NL80211_BAND_5GHZ) {
+               bgscan->mgmt_rate = cpu_to_le16(RSI_RATE_6);
+               bgscan->def_chan = cpu_to_le16(40);
+       } else {
+               bgscan->mgmt_rate = cpu_to_le16(RSI_RATE_1);
+               bgscan->def_chan = cpu_to_le16(11);
+       }
+       bgscan->channel_scan_time = cpu_to_le16(RSI_CHANNEL_SCAN_TIME);
+
+       probereq_skb = ieee80211_probereq_get(common->priv->hw, vif->addr, ssid,
+                                             ssid_len, scan_req->ie_len);
+
+       memcpy(&skb->data[frame_len], probereq_skb->data, probereq_skb->len);
+
+       bgscan->probe_req_length = cpu_to_le16(probereq_skb->len);
+
+       rsi_set_len_qno(&bgscan->desc_dword0.len_qno,
+                       (frame_len - FRAME_DESC_SZ + probereq_skb->len),
+                       RSI_WIFI_MGMT_Q);
+
+       skb_put(skb, frame_len + probereq_skb->len);
+
+       dev_kfree_skb(probereq_skb);
+
+       return rsi_send_internal_mgmt_frame(common, skb);
+}
+
 /**
  * rsi_handle_ta_confirm_type() - This function handles the confirm frames.
  * @common: Pointer to the driver private structure.
@@ -1771,9 +1885,28 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
                        return 0;
                }
                break;
+
+       case SCAN_REQUEST:
+               rsi_dbg(INFO_ZONE, "Set channel confirm\n");
+               break;
+
        case WAKEUP_SLEEP_REQUEST:
                rsi_dbg(INFO_ZONE, "Wakeup/Sleep confirmation.\n");
                return rsi_handle_ps_confirm(adapter, msg);
+
+       case BG_SCAN_PROBE_REQ:
+               rsi_dbg(INFO_ZONE, "BG scan complete event\n");
+               if (common->bgscan_en) {
+                       struct cfg80211_scan_info info;
+
+                       if (!rsi_send_bgscan_params(common, RSI_STOP_BGSCAN))
+                               common->bgscan_en = 0;
+                       info.aborted = false;
+                       ieee80211_scan_completed(adapter->hw, &info);
+               }
+               rsi_dbg(INFO_ZONE, "Background scan completed\n");
+               break;
+
        default:
                rsi_dbg(INFO_ZONE, "%s: Invalid TA confirm pkt received\n",
                        __func__);
index 5733e44..b412b65 100644 (file)
@@ -230,16 +230,19 @@ static void rsi_reset_card(struct sdio_func *pfunction)
                rsi_dbg(ERR_ZONE, "%s: CMD0 failed : %d\n", __func__, err);
 
        /* Issue CMD5, arg = 0 */
-       err = rsi_issue_sdiocommand(pfunction,  SD_IO_SEND_OP_COND, 0,
-                                   (MMC_RSP_R4 | MMC_CMD_BCR), &resp);
-       if (err)
-               rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n", __func__, err);
-       card->ocr = resp;
+       if (!host->ocr_avail) {
+               err = rsi_issue_sdiocommand(pfunction,  SD_IO_SEND_OP_COND, 0,
+                                           (MMC_RSP_R4 | MMC_CMD_BCR), &resp);
+               if (err)
+                       rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n",
+                               __func__, err);
 
+               host->ocr_avail = resp;
+       }
        /* Issue CMD5, arg = ocr. Wait till card is ready  */
        for (i = 0; i < 100; i++) {
                err = rsi_issue_sdiocommand(pfunction, SD_IO_SEND_OP_COND,
-                                           card->ocr,
+                                           host->ocr_avail,
                                            (MMC_RSP_R4 | MMC_CMD_BCR), &resp);
                if (err) {
                        rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n",
index a084f22..4dc0c01 100644 (file)
@@ -164,6 +164,24 @@ struct transmit_q_stats {
        u32 total_tx_pkt_freed[NUM_EDCA_QUEUES + 2];
 };
 
+#define MAX_BGSCAN_CHANNELS_DUAL_BAND  38
+#define MAX_BGSCAN_PROBE_REQ_LEN       0x64
+#define RSI_DEF_BGSCAN_THRLD           0x0
+#define RSI_DEF_ROAM_THRLD             0xa
+#define RSI_BGSCAN_PERIODICITY         0x1e
+#define RSI_ACTIVE_SCAN_TIME           0x14
+#define RSI_PASSIVE_SCAN_TIME          0x46
+#define RSI_CHANNEL_SCAN_TIME          20
+struct rsi_bgscan_params {
+       u16 bgscan_threshold;
+       u16 roam_threshold;
+       u16 bgscan_periodicity;
+       u8 num_bgscan_channels;
+       u8 two_probe;
+       u16 active_scan_duration;
+       u16 passive_scan_duration;
+};
+
 struct vif_priv {
        bool is_ht;
        bool sgi;
@@ -289,6 +307,10 @@ struct rsi_common {
 
        bool eapol4_confirm;
        void *bt_adapter;
+
+       struct cfg80211_scan_request *hwscan;
+       struct rsi_bgscan_params bgscan;
+       u8 bgscan_en;
 };
 
 struct eepromrw_info {
index 359fbdf..ea83faa 100644 (file)
 #define RSI_MAX_TX_AGGR_FRMS           8
 #define RSI_MAX_RX_AGGR_FRMS           8
 
+#define RSI_MAX_SCAN_SSIDS             16
+#define RSI_MAX_SCAN_IE_LEN            256
+
 enum opmode {
        RSI_OPMODE_UNSUPPORTED = -1,
        RSI_OPMODE_AP = 0,
@@ -623,6 +626,34 @@ struct rsi_wowlan_req {
        u16 host_sleep_status;
 } __packed;
 
+#define RSI_START_BGSCAN               1
+#define RSI_STOP_BGSCAN                        0
+#define HOST_BG_SCAN_TRIG              BIT(4)
+struct rsi_bgscan_config {
+       struct rsi_cmd_desc_dword0 desc_dword0;
+       __le64 reserved;
+       __le32 reserved1;
+       __le16 bgscan_threshold;
+       __le16 roam_threshold;
+       __le16 bgscan_periodicity;
+       u8 num_bgscan_channels;
+       u8 two_probe;
+       __le16 active_scan_duration;
+       __le16 passive_scan_duration;
+       __le16 channels2scan[MAX_BGSCAN_CHANNELS_DUAL_BAND];
+} __packed;
+
+struct rsi_bgscan_probe {
+       struct rsi_cmd_desc_dword0 desc_dword0;
+       __le64 reserved;
+       __le32 reserved1;
+       __le16 mgmt_rate;
+       __le16 flags;
+       __le16 def_chan;
+       __le16 channel_scan_time;
+       __le16 probe_req_length;
+} __packed;
+
 static inline u32 rsi_get_queueno(u8 *addr, u16 offset)
 {
        return (le16_to_cpu(*(__le16 *)&addr[offset]) & 0x7000) >> 12;
@@ -694,4 +725,8 @@ int rsi_send_wowlan_request(struct rsi_common *common, u16 flags,
 #endif
 int rsi_send_ps_request(struct rsi_hw *adapter, bool enable,
                        struct ieee80211_vif *vif);
+void init_bgscan_params(struct rsi_common *common);
+int rsi_send_bgscan_params(struct rsi_common *common, int enable);
+int rsi_send_bgscan_probe_req(struct rsi_common *common,
+                             struct ieee80211_vif *vif);
 #endif
index 2231ba0..d94266d 100644 (file)
@@ -371,28 +371,14 @@ int cw1200_debug_init(struct cw1200_common *priv)
 
        d->debugfs_phy = debugfs_create_dir("cw1200",
                                            priv->hw->wiphy->debugfsdir);
-       if (!d->debugfs_phy)
-               goto err;
-
-       if (!debugfs_create_file("status", 0400, d->debugfs_phy,
-                                priv, &cw1200_status_fops))
-               goto err;
-
-       if (!debugfs_create_file("counters", 0400, d->debugfs_phy,
-                                priv, &cw1200_counters_fops))
-               goto err;
-
-       if (!debugfs_create_file("wsm_dumps", 0200, d->debugfs_phy,
-                                priv, &fops_wsm_dumps))
-               goto err;
+       debugfs_create_file("status", 0400, d->debugfs_phy, priv,
+                           &cw1200_status_fops);
+       debugfs_create_file("counters", 0400, d->debugfs_phy, priv,
+                           &cw1200_counters_fops);
+       debugfs_create_file("wsm_dumps", 0200, d->debugfs_phy, priv,
+                           &fops_wsm_dumps);
 
        return 0;
-
-err:
-       priv->debug = NULL;
-       debugfs_remove_recursive(d->debugfs_phy);
-       kfree(d);
-       return ret;
 }
 
 void cw1200_debug_release(struct cw1200_common *priv)
index 30e7646..b788123 100644 (file)
@@ -465,8 +465,8 @@ int cw1200_load_firmware(struct cw1200_common *priv)
 
        if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) {
                pr_err("Device is already in QUEUE mode!\n");
-                       ret = -EINVAL;
-                       goto out;
+               ret = -EINVAL;
+               goto out;
        }
 
        switch (priv->hw_type)  {
index 7c31b63..7895efe 100644 (file)
@@ -283,7 +283,6 @@ int cw1200_queue_put(struct cw1200_queue *queue,
                     struct cw1200_txpriv *txpriv)
 {
        int ret = 0;
-       LIST_HEAD(gc_list);
        struct cw1200_queue_stats *stats = queue->stats;
 
        if (txpriv->link_id >= queue->stats->map_capacity)
index 0a9eac9..71e9b91 100644 (file)
@@ -84,8 +84,11 @@ int cw1200_hw_scan(struct ieee80211_hw *hw,
 
        frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
                req->ie_len);
-       if (!frame.skb)
+       if (!frame.skb) {
+               mutex_unlock(&priv->conf_mutex);
+               up(&priv->scan.lock);
                return -ENOMEM;
+       }
 
        if (req->ie_len)
                skb_put_data(frame.skb, req->ie, req->ie_len);
index 448da1f..c99b23a 100644 (file)
@@ -54,11 +54,6 @@ static const struct file_operations name## _ops = {                  \
 #define DEBUGFS_ADD(name, parent)                                      \
        wl->debugfs.name = debugfs_create_file(#name, 0400, parent,     \
                                               wl, &name## _ops);       \
-       if (IS_ERR(wl->debugfs.name)) {                                 \
-               ret = PTR_ERR(wl->debugfs.name);                        \
-               wl->debugfs.name = NULL;                                \
-               goto out;                                               \
-       }
 
 #define DEBUGFS_DEL(name)                                              \
        do {                                                            \
@@ -354,10 +349,8 @@ static void wl1251_debugfs_delete_files(struct wl1251 *wl)
        DEBUGFS_DEL(excessive_retries);
 }
 
-static int wl1251_debugfs_add_files(struct wl1251 *wl)
+static void wl1251_debugfs_add_files(struct wl1251 *wl)
 {
-       int ret = 0;
-
        DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow);
 
        DEBUGFS_FWSTATS_ADD(rx, out_of_mem);
@@ -453,12 +446,6 @@ static int wl1251_debugfs_add_files(struct wl1251 *wl)
        DEBUGFS_ADD(tx_queue_status, wl->debugfs.rootdir);
        DEBUGFS_ADD(retry_count, wl->debugfs.rootdir);
        DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir);
-
-out:
-       if (ret < 0)
-               wl1251_debugfs_delete_files(wl);
-
-       return ret;
 }
 
 void wl1251_debugfs_reset(struct wl1251 *wl)
@@ -471,56 +458,20 @@ void wl1251_debugfs_reset(struct wl1251 *wl)
 
 int wl1251_debugfs_init(struct wl1251 *wl)
 {
-       int ret;
+       wl->stats.fw_stats = kzalloc(sizeof(*wl->stats.fw_stats), GFP_KERNEL);
+       if (!wl->stats.fw_stats)
+               return -ENOMEM;
 
        wl->debugfs.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
 
-       if (IS_ERR(wl->debugfs.rootdir)) {
-               ret = PTR_ERR(wl->debugfs.rootdir);
-               wl->debugfs.rootdir = NULL;
-               goto err;
-       }
-
        wl->debugfs.fw_statistics = debugfs_create_dir("fw-statistics",
                                                       wl->debugfs.rootdir);
 
-       if (IS_ERR(wl->debugfs.fw_statistics)) {
-               ret = PTR_ERR(wl->debugfs.fw_statistics);
-               wl->debugfs.fw_statistics = NULL;
-               goto err_root;
-       }
-
-       wl->stats.fw_stats = kzalloc(sizeof(*wl->stats.fw_stats),
-                                     GFP_KERNEL);
-
-       if (!wl->stats.fw_stats) {
-               ret = -ENOMEM;
-               goto err_fw;
-       }
-
        wl->stats.fw_stats_update = jiffies;
 
-       ret = wl1251_debugfs_add_files(wl);
-
-       if (ret < 0)
-               goto err_file;
+       wl1251_debugfs_add_files(wl);
 
        return 0;
-
-err_file:
-       kfree(wl->stats.fw_stats);
-       wl->stats.fw_stats = NULL;
-
-err_fw:
-       debugfs_remove(wl->debugfs.fw_statistics);
-       wl->debugfs.fw_statistics = NULL;
-
-err_root:
-       debugfs_remove(wl->debugfs.rootdir);
-       wl->debugfs.rootdir = NULL;
-
-err:
-       return ret;
 }
 
 void wl1251_debugfs_exit(struct wl1251 *wl)
index 0521cbf..6c3c04e 100644 (file)
@@ -125,20 +125,10 @@ WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, "%u");
 int wl12xx_debugfs_add_files(struct wl1271 *wl,
                             struct dentry *rootdir)
 {
-       int ret = 0;
-       struct dentry *entry, *stats, *moddir;
+       struct dentry *stats, *moddir;
 
        moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir);
-       if (!moddir || IS_ERR(moddir)) {
-               entry = moddir;
-               goto err;
-       }
-
        stats = debugfs_create_dir("fw_stats", moddir);
-       if (!stats || IS_ERR(stats)) {
-               entry = stats;
-               goto err;
-       }
 
        DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow);
 
@@ -232,12 +222,4 @@ int wl12xx_debugfs_add_files(struct wl1271 *wl,
        DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data);
 
        return 0;
-
-err:
-       if (IS_ERR(entry))
-               ret = PTR_ERR(entry);
-       else
-               ret = -ENOMEM;
-
-       return ret;
 }
index 597e934..5f4ec99 100644 (file)
@@ -422,20 +422,10 @@ static const struct file_operations radar_debug_mode_ops = {
 int wl18xx_debugfs_add_files(struct wl1271 *wl,
                             struct dentry *rootdir)
 {
-       int ret = 0;
-       struct dentry *entry, *stats, *moddir;
+       struct dentry *stats, *moddir;
 
        moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir);
-       if (!moddir || IS_ERR(moddir)) {
-               entry = moddir;
-               goto err;
-       }
-
        stats = debugfs_create_dir("fw_stats", moddir);
-       if (!stats || IS_ERR(stats)) {
-               entry = stats;
-               goto err;
-       }
 
        DEBUGFS_ADD(clear_fw_stats, stats);
 
@@ -590,12 +580,4 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl,
        DEBUGFS_ADD(dynamic_fw_traces, moddir);
 
        return 0;
-
-err:
-       if (IS_ERR(entry))
-               ret = PTR_ERR(entry);
-       else
-               ret = -ENOMEM;
-
-       return ret;
 }
index 9039687..348be0a 100644 (file)
@@ -1427,7 +1427,7 @@ int wl1271_cmd_set_sta_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
                wl1271_warning("could not set keys");
-       goto out;
+               goto out;
        }
 
 out:
index aeb74e7..68acd90 100644 (file)
@@ -1301,11 +1301,10 @@ static const struct file_operations fw_logger_ops = {
        .llseek = default_llseek,
 };
 
-static int wl1271_debugfs_add_files(struct wl1271 *wl,
-                                   struct dentry *rootdir)
+static void wl1271_debugfs_add_files(struct wl1271 *wl,
+                                    struct dentry *rootdir)
 {
-       int ret = 0;
-       struct dentry *entry, *streaming;
+       struct dentry *streaming;
 
        DEBUGFS_ADD(tx_queue_len, rootdir);
        DEBUGFS_ADD(retry_count, rootdir);
@@ -1330,23 +1329,11 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
        DEBUGFS_ADD(fw_logger, rootdir);
 
        streaming = debugfs_create_dir("rx_streaming", rootdir);
-       if (!streaming || IS_ERR(streaming))
-               goto err;
 
        DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming);
        DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming);
 
        DEBUGFS_ADD_PREFIX(dev, mem, rootdir);
-
-       return 0;
-
-err:
-       if (IS_ERR(entry))
-               ret = PTR_ERR(entry);
-       else
-               ret = -ENOMEM;
-
-       return ret;
 }
 
 void wl1271_debugfs_reset(struct wl1271 *wl)
@@ -1367,11 +1354,6 @@ int wl1271_debugfs_init(struct wl1271 *wl)
        rootdir = debugfs_create_dir(KBUILD_MODNAME,
                                     wl->hw->wiphy->debugfsdir);
 
-       if (IS_ERR(rootdir)) {
-               ret = PTR_ERR(rootdir);
-               goto out;
-       }
-
        wl->stats.fw_stats = kzalloc(wl->stats.fw_stats_len, GFP_KERNEL);
        if (!wl->stats.fw_stats) {
                ret = -ENOMEM;
@@ -1380,9 +1362,7 @@ int wl1271_debugfs_init(struct wl1271 *wl)
 
        wl->stats.fw_stats_update = jiffies;
 
-       ret = wl1271_debugfs_add_files(wl, rootdir);
-       if (ret < 0)
-               goto out_exit;
+       wl1271_debugfs_add_files(wl, rootdir);
 
        ret = wlcore_debugfs_init(wl, rootdir);
        if (ret < 0)
index bf14676..a4952c4 100644 (file)
@@ -53,19 +53,15 @@ static const struct file_operations name## _ops = {                 \
 
 #define DEBUGFS_ADD(name, parent)                                      \
        do {                                                            \
-               entry = debugfs_create_file(#name, 0400, parent,        \
-                                           wl, &name## _ops);          \
-               if (!entry || IS_ERR(entry))                            \
-                       goto err;                                       \
+               debugfs_create_file(#name, 0400, parent,                \
+                                   wl, &name## _ops);                  \
        } while (0)
 
 
 #define DEBUGFS_ADD_PREFIX(prefix, name, parent)                       \
        do {                                                            \
-               entry = debugfs_create_file(#name, 0400, parent,        \
+               debugfs_create_file(#name, 0400, parent,                \
                                    wl, &prefix## _## name## _ops);     \
-               if (!entry || IS_ERR(entry))                            \
-                       goto err;                                       \
        } while (0)
 
 #define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type)              \
index 26b1873..2e12de8 100644 (file)
@@ -1085,8 +1085,11 @@ static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
                goto out;
 
        ret = wl12xx_fetch_firmware(wl, plt);
-       if (ret < 0)
-               goto out;
+       if (ret < 0) {
+               kfree(wl->fw_status);
+               kfree(wl->raw_fw_status);
+               kfree(wl->tx_res_if);
+       }
 
 out:
        return ret;
index bd10165..4d4b077 100644 (file)
@@ -164,6 +164,12 @@ static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue)
        }
 
        sdio_claim_host(func);
+       /*
+        * To guarantee that the SDIO card is power cycled, as required to make
+        * the FW programming to succeed, let's do a brute force HW reset.
+        */
+       mmc_hw_reset(card->host);
+
        sdio_enable_func(func);
        sdio_release_host(func);
 
@@ -174,20 +180,13 @@ static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue)
 {
        struct sdio_func *func = dev_to_sdio_func(glue->dev);
        struct mmc_card *card = func->card;
-       int error;
 
        sdio_claim_host(func);
        sdio_disable_func(func);
        sdio_release_host(func);
 
        /* Let runtime PM know the card is powered off */
-       error = pm_runtime_put(&card->dev);
-       if (error < 0 && error != -EBUSY) {
-               dev_err(&card->dev, "%s failed: %i\n", __func__, error);
-
-               return error;
-       }
-
+       pm_runtime_put(&card->dev);
        return 0;
 }
 
index 3a93e4d..71044c6 100644 (file)
 #include <linux/etherdevice.h>
 #include <linux/module.h>
 
-#include <net/cfg80211.h>
-#include <net/rtnetlink.h>
-#include <linux/etherdevice.h>
-#include <linux/module.h>
-
 static struct wiphy *common_wiphy;
 
 struct virt_wifi_wiphy_priv {
@@ -429,13 +424,11 @@ static int virt_wifi_net_device_open(struct net_device *dev)
 static int virt_wifi_net_device_stop(struct net_device *dev)
 {
        struct virt_wifi_netdev_priv *n_priv = netdev_priv(dev);
-       struct virt_wifi_wiphy_priv *w_priv;
 
        n_priv->is_up = false;
 
        if (!dev->ieee80211_ptr)
                return 0;
-       w_priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
 
        virt_wifi_cancel_scan(dev->ieee80211_ptr->wiphy);
        virt_wifi_cancel_connect(dev);
index 2625740..330ddb6 100644 (file)
@@ -655,7 +655,7 @@ static void frontend_changed(struct xenbus_device *dev,
                set_backend_state(be, XenbusStateClosed);
                if (xenbus_dev_is_online(dev))
                        break;
-               /* fall through if not online */
+               /* fall through if not online */
        case XenbusStateUnknown:
                set_backend_state(be, XenbusStateClosed);
                device_unregister(&dev->dev);
index 150e497..6a9dd68 100644 (file)
@@ -1253,6 +1253,7 @@ static u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
         * effects say only one namespace is affected.
         */
        if (effects & (NVME_CMD_EFFECTS_LBCC | NVME_CMD_EFFECTS_CSE_MASK)) {
+               mutex_lock(&ctrl->scan_lock);
                nvme_start_freeze(ctrl);
                nvme_wait_freeze(ctrl);
        }
@@ -1281,8 +1282,10 @@ static void nvme_passthru_end(struct nvme_ctrl *ctrl, u32 effects)
         */
        if (effects & NVME_CMD_EFFECTS_LBCC)
                nvme_update_formats(ctrl);
-       if (effects & (NVME_CMD_EFFECTS_LBCC | NVME_CMD_EFFECTS_CSE_MASK))
+       if (effects & (NVME_CMD_EFFECTS_LBCC | NVME_CMD_EFFECTS_CSE_MASK)) {
                nvme_unfreeze(ctrl);
+               mutex_unlock(&ctrl->scan_lock);
+       }
        if (effects & NVME_CMD_EFFECTS_CCC)
                nvme_init_identify(ctrl);
        if (effects & (NVME_CMD_EFFECTS_NIC | NVME_CMD_EFFECTS_NCC))
@@ -3401,6 +3404,7 @@ static void nvme_scan_work(struct work_struct *work)
        if (nvme_identify_ctrl(ctrl, &id))
                return;
 
+       mutex_lock(&ctrl->scan_lock);
        nn = le32_to_cpu(id->nn);
        if (ctrl->vs >= NVME_VS(1, 1, 0) &&
            !(ctrl->quirks & NVME_QUIRK_IDENTIFY_CNS)) {
@@ -3409,6 +3413,7 @@ static void nvme_scan_work(struct work_struct *work)
        }
        nvme_scan_ns_sequential(ctrl, nn);
 out_free_id:
+       mutex_unlock(&ctrl->scan_lock);
        kfree(id);
        down_write(&ctrl->namespaces_rwsem);
        list_sort(NULL, &ctrl->namespaces, ns_cmp);
@@ -3652,6 +3657,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
 
        ctrl->state = NVME_CTRL_NEW;
        spin_lock_init(&ctrl->lock);
+       mutex_init(&ctrl->scan_lock);
        INIT_LIST_HEAD(&ctrl->namespaces);
        init_rwsem(&ctrl->namespaces_rwsem);
        ctrl->dev = dev;
index ab961bd..c4a1bb4 100644 (file)
@@ -154,6 +154,7 @@ struct nvme_ctrl {
        enum nvme_ctrl_state state;
        bool identified;
        spinlock_t lock;
+       struct mutex scan_lock;
        const struct nvme_ctrl_ops *ops;
        struct request_queue *admin_q;
        struct request_queue *connect_q;
index 9bc5854..7fee665 100644 (file)
@@ -2557,27 +2557,18 @@ static void nvme_reset_work(struct work_struct *work)
        if (dev->ctrl.ctrl_config & NVME_CC_ENABLE)
                nvme_dev_disable(dev, false);
 
-       /*
-        * Introduce CONNECTING state from nvme-fc/rdma transports to mark the
-        * initializing procedure here.
-        */
-       if (!nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_CONNECTING)) {
-               dev_warn(dev->ctrl.device,
-                       "failed to mark controller CONNECTING\n");
-               goto out;
-       }
-
+       mutex_lock(&dev->shutdown_lock);
        result = nvme_pci_enable(dev);
        if (result)
-               goto out;
+               goto out_unlock;
 
        result = nvme_pci_configure_admin_queue(dev);
        if (result)
-               goto out;
+               goto out_unlock;
 
        result = nvme_alloc_admin_tags(dev);
        if (result)
-               goto out;
+               goto out_unlock;
 
        /*
         * Limit the max command size to prevent iod->sg allocations going
@@ -2585,6 +2576,17 @@ static void nvme_reset_work(struct work_struct *work)
         */
        dev->ctrl.max_hw_sectors = NVME_MAX_KB_SZ << 1;
        dev->ctrl.max_segments = NVME_MAX_SEGS;
+       mutex_unlock(&dev->shutdown_lock);
+
+       /*
+        * Introduce CONNECTING state from nvme-fc/rdma transports to mark the
+        * initializing procedure here.
+        */
+       if (!nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_CONNECTING)) {
+               dev_warn(dev->ctrl.device,
+                       "failed to mark controller CONNECTING\n");
+               goto out;
+       }
 
        result = nvme_init_identify(&dev->ctrl);
        if (result)
@@ -2649,6 +2651,8 @@ static void nvme_reset_work(struct work_struct *work)
        nvme_start_ctrl(&dev->ctrl);
        return;
 
+ out_unlock:
+       mutex_unlock(&dev->shutdown_lock);
  out:
        nvme_remove_dead_ctrl(dev, result);
 }
index 5ad1342..de61573 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/phy.h>
 #include <linux/phy_fixed.h>
 #include <linux/of.h>
-#include <linux/of_gpio.h>
 #include <linux/of_irq.h>
 #include <linux/of_mdio.h>
 #include <linux/of_net.h>
@@ -463,7 +462,6 @@ int of_phy_register_fixed_link(struct device_node *np)
        struct device_node *fixed_link_node;
        u32 fixed_link_prop[5];
        const char *managed;
-       int link_gpio = -1;
 
        if (of_property_read_string(np, "managed", &managed) == 0 &&
            strcmp(managed, "in-band-status") == 0) {
@@ -485,11 +483,7 @@ int of_phy_register_fixed_link(struct device_node *np)
                status.pause = of_property_read_bool(fixed_link_node, "pause");
                status.asym_pause = of_property_read_bool(fixed_link_node,
                                                          "asym-pause");
-               link_gpio = of_get_named_gpio_flags(fixed_link_node,
-                                                   "link-gpios", 0, NULL);
                of_node_put(fixed_link_node);
-               if (link_gpio == -EPROBE_DEFER)
-                       return -EPROBE_DEFER;
 
                goto register_phy;
        }
@@ -508,8 +502,7 @@ int of_phy_register_fixed_link(struct device_node *np)
        return -ENODEV;
 
 register_phy:
-       return PTR_ERR_OR_ZERO(fixed_phy_register(PHY_POLL, &status, link_gpio,
-                                                 np));
+       return PTR_ERR_OR_ZERO(fixed_phy_register(PHY_POLL, &status, np));
 }
 EXPORT_SYMBOL(of_phy_register_fixed_link);
 
index 52e47da..80f8430 100644 (file)
@@ -310,6 +310,9 @@ static int imx6_pcie_attach_pd(struct device *dev)
        imx6_pcie->pd_pcie = dev_pm_domain_attach_by_name(dev, "pcie");
        if (IS_ERR(imx6_pcie->pd_pcie))
                return PTR_ERR(imx6_pcie->pd_pcie);
+       /* Do nothing when power domain missing */
+       if (!imx6_pcie->pd_pcie)
+               return 0;
        link = device_link_add(dev, imx6_pcie->pd_pcie,
                        DL_FLAG_STATELESS |
                        DL_FLAG_PM_RUNTIME |
@@ -323,13 +326,13 @@ static int imx6_pcie_attach_pd(struct device *dev)
        if (IS_ERR(imx6_pcie->pd_pcie_phy))
                return PTR_ERR(imx6_pcie->pd_pcie_phy);
 
-       device_link_add(dev, imx6_pcie->pd_pcie_phy,
+       link = device_link_add(dev, imx6_pcie->pd_pcie_phy,
                        DL_FLAG_STATELESS |
                        DL_FLAG_PM_RUNTIME |
                        DL_FLAG_RPM_ACTIVE);
-       if (IS_ERR(link)) {
-               dev_err(dev, "Failed to add device_link to pcie_phy pd: %ld\n", PTR_ERR(link));
-               return PTR_ERR(link);
+       if (!link) {
+               dev_err(dev, "Failed to add device_link to pcie_phy pd.\n");
+               return -EINVAL;
        }
 
        return 0;
index b171b6b..0c389a3 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/resource.h>
 #include <linux/of_pci.h>
 #include <linux/of_irq.h>
-#include <linux/gpio/consumer.h>
 
 #include "pcie-designware.h"
 
@@ -30,7 +29,6 @@ struct armada8k_pcie {
        struct dw_pcie *pci;
        struct clk *clk;
        struct clk *clk_reg;
-       struct gpio_desc *reset_gpio;
 };
 
 #define PCIE_VENDOR_REGS_OFFSET                0x8000
@@ -139,12 +137,6 @@ static int armada8k_pcie_host_init(struct pcie_port *pp)
        struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
        struct armada8k_pcie *pcie = to_armada8k_pcie(pci);
 
-       if (pcie->reset_gpio) {
-               /* assert and then deassert the reset signal */
-               gpiod_set_value_cansleep(pcie->reset_gpio, 1);
-               msleep(100);
-               gpiod_set_value_cansleep(pcie->reset_gpio, 0);
-       }
        dw_pcie_setup_rc(pp);
        armada8k_pcie_establish_link(pcie);
 
@@ -257,14 +249,6 @@ static int armada8k_pcie_probe(struct platform_device *pdev)
                goto fail_clkreg;
        }
 
-       /* Get reset gpio signal and hold asserted (logically high) */
-       pcie->reset_gpio = devm_gpiod_get_optional(dev, "reset",
-                                                  GPIOD_OUT_HIGH);
-       if (IS_ERR(pcie->reset_gpio)) {
-               ret = PTR_ERR(pcie->reset_gpio);
-               goto fail_clkreg;
-       }
-
        platform_set_drvdata(pdev, pcie);
 
        ret = armada8k_add_pcie_port(pcie, pdev);
index b0a413f..e2a879e 100644 (file)
@@ -639,8 +639,9 @@ static void quirk_synopsys_haps(struct pci_dev *pdev)
                break;
        }
 }
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SYNOPSYS, PCI_ANY_ID,
-                        quirk_synopsys_haps);
+DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_SYNOPSYS, PCI_ANY_ID,
+                              PCI_CLASS_SERIAL_USB_XHCI, 0,
+                              quirk_synopsys_haps);
 
 /*
  * Let's make the southbridge information explicit instead of having to
index 6fb4b56..224ea4e 100644 (file)
@@ -21,6 +21,16 @@ config PHY_BERLIN_USB
        help
          Enable this to support the USB PHY on Marvell Berlin SoCs.
 
+config PHY_MVEBU_A38X_COMPHY
+       tristate "Marvell Armada 38x comphy driver"
+       depends on ARCH_MVEBU || COMPILE_TEST
+       depends on OF
+       select GENERIC_PHY
+       help
+         This driver allows to control the comphy, an hardware block providing
+         shared serdes PHYs on Marvell Armada 38x. Its serdes lanes can be
+         used by various controllers (Ethernet, sata, usb, PCIe...).
+
 config PHY_MVEBU_CP110_COMPHY
        tristate "Marvell CP110 comphy driver"
        depends on ARCH_MVEBU || COMPILE_TEST
index 3975b14..59b6c03 100644 (file)
@@ -2,6 +2,7 @@
 obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
 obj-$(CONFIG_PHY_BERLIN_SATA)          += phy-berlin-sata.o
 obj-$(CONFIG_PHY_BERLIN_USB)           += phy-berlin-usb.o
+obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY)    += phy-armada38x-comphy.o
 obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY)   += phy-mvebu-cp110-comphy.o
 obj-$(CONFIG_PHY_MVEBU_SATA)           += phy-mvebu-sata.o
 obj-$(CONFIG_PHY_PXA_28NM_HSIC)                += phy-pxa-28nm-hsic.o
diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c
new file mode 100644 (file)
index 0000000..3e00bc6
--- /dev/null
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Russell King, Deep Blue Solutions Ltd.
+ *
+ * Partly derived from CP110 comphy driver by Antoine Tenart
+ * <antoine.tenart@bootlin.com>
+ */
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+
+#define MAX_A38X_COMPHY        6
+#define MAX_A38X_PORTS 3
+
+#define COMPHY_CFG1            0x00
+#define  COMPHY_CFG1_GEN_TX(x)         ((x) << 26)
+#define  COMPHY_CFG1_GEN_TX_MSK                COMPHY_CFG1_GEN_TX(15)
+#define  COMPHY_CFG1_GEN_RX(x)         ((x) << 22)
+#define  COMPHY_CFG1_GEN_RX_MSK                COMPHY_CFG1_GEN_RX(15)
+#define  GEN_SGMII_1_25GBPS            6
+#define  GEN_SGMII_3_125GBPS           8
+
+#define COMPHY_STAT1           0x18
+#define  COMPHY_STAT1_PLL_RDY_TX       BIT(3)
+#define  COMPHY_STAT1_PLL_RDY_RX       BIT(2)
+
+#define COMPHY_SELECTOR                0xfc
+
+struct a38x_comphy;
+
+struct a38x_comphy_lane {
+       void __iomem *base;
+       struct a38x_comphy *priv;
+       unsigned int n;
+
+       int port;
+};
+
+struct a38x_comphy {
+       void __iomem *base;
+       struct device *dev;
+       struct a38x_comphy_lane lane[MAX_A38X_COMPHY];
+};
+
+static const u8 gbe_mux[MAX_A38X_COMPHY][MAX_A38X_PORTS] = {
+       { 0, 0, 0 },
+       { 4, 5, 0 },
+       { 0, 4, 0 },
+       { 0, 0, 4 },
+       { 0, 3, 0 },
+       { 0, 0, 3 },
+};
+
+static void a38x_comphy_set_reg(struct a38x_comphy_lane *lane,
+                               unsigned int offset, u32 mask, u32 value)
+{
+       u32 val;
+
+       val = readl_relaxed(lane->base + offset) & ~mask;
+       writel(val | value, lane->base + offset);
+}
+
+static void a38x_comphy_set_speed(struct a38x_comphy_lane *lane,
+                                 unsigned int gen_tx, unsigned int gen_rx)
+{
+       a38x_comphy_set_reg(lane, COMPHY_CFG1,
+                           COMPHY_CFG1_GEN_TX_MSK | COMPHY_CFG1_GEN_RX_MSK,
+                           COMPHY_CFG1_GEN_TX(gen_tx) |
+                           COMPHY_CFG1_GEN_RX(gen_rx));
+}
+
+static int a38x_comphy_poll(struct a38x_comphy_lane *lane,
+                           unsigned int offset, u32 mask, u32 value)
+{
+       u32 val;
+       int ret;
+
+       ret = readl_relaxed_poll_timeout_atomic(lane->base + offset, val,
+                                               (val & mask) == value,
+                                               1000, 150000);
+
+       if (ret)
+               dev_err(lane->priv->dev,
+                       "comphy%u: timed out waiting for status\n", lane->n);
+
+       return ret;
+}
+
+/*
+ * We only support changing the speed for comphys configured for GBE.
+ * Since that is all we do, we only poll for PLL ready status.
+ */
+static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub)
+{
+       struct a38x_comphy_lane *lane = phy_get_drvdata(phy);
+       unsigned int gen;
+
+       if (mode != PHY_MODE_ETHERNET)
+               return -EINVAL;
+
+       switch (sub) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+               gen = GEN_SGMII_1_25GBPS;
+               break;
+
+       case PHY_INTERFACE_MODE_2500BASEX:
+               gen = GEN_SGMII_3_125GBPS;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       a38x_comphy_set_speed(lane, gen, gen);
+
+       return a38x_comphy_poll(lane, COMPHY_STAT1,
+                               COMPHY_STAT1_PLL_RDY_TX |
+                               COMPHY_STAT1_PLL_RDY_RX,
+                               COMPHY_STAT1_PLL_RDY_TX |
+                               COMPHY_STAT1_PLL_RDY_RX);
+}
+
+static const struct phy_ops a38x_comphy_ops = {
+       .set_mode       = a38x_comphy_set_mode,
+       .owner          = THIS_MODULE,
+};
+
+static struct phy *a38x_comphy_xlate(struct device *dev,
+                                    struct of_phandle_args *args)
+{
+       struct a38x_comphy_lane *lane;
+       struct phy *phy;
+       u32 val;
+
+       if (WARN_ON(args->args[0] >= MAX_A38X_PORTS))
+               return ERR_PTR(-EINVAL);
+
+       phy = of_phy_simple_xlate(dev, args);
+       if (IS_ERR(phy))
+               return phy;
+
+       lane = phy_get_drvdata(phy);
+       if (lane->port >= 0)
+               return ERR_PTR(-EBUSY);
+
+       lane->port = args->args[0];
+
+       val = readl_relaxed(lane->priv->base + COMPHY_SELECTOR);
+       val = (val >> (4 * lane->n)) & 0xf;
+
+       if (!gbe_mux[lane->n][lane->port] ||
+           val != gbe_mux[lane->n][lane->port]) {
+               dev_warn(lane->priv->dev,
+                        "comphy%u: not configured for GBE\n", lane->n);
+               phy = ERR_PTR(-EINVAL);
+       }
+
+       return phy;
+}
+
+static int a38x_comphy_probe(struct platform_device *pdev)
+{
+       struct phy_provider *provider;
+       struct device_node *child;
+       struct a38x_comphy *priv;
+       struct resource *res;
+       void __iomem *base;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       priv->dev = &pdev->dev;
+       priv->base = base;
+
+       for_each_available_child_of_node(pdev->dev.of_node, child) {
+               struct phy *phy;
+               int ret;
+               u32 val;
+
+               ret = of_property_read_u32(child, "reg", &val);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "missing 'reg' property (%d)\n",
+                               ret);
+                       continue;
+               }
+
+               if (val >= MAX_A38X_COMPHY || priv->lane[val].base) {
+                       dev_err(&pdev->dev, "invalid 'reg' property\n");
+                       continue;
+               }
+
+               phy = devm_phy_create(&pdev->dev, child, &a38x_comphy_ops);
+               if (IS_ERR(phy))
+                       return PTR_ERR(phy);
+
+               priv->lane[val].base = base + 0x28 * val;
+               priv->lane[val].priv = priv;
+               priv->lane[val].n = val;
+               priv->lane[val].port = -1;
+               phy_set_drvdata(phy, &priv->lane[val]);
+       }
+
+       dev_set_drvdata(&pdev->dev, priv);
+
+       provider = devm_of_phy_provider_register(&pdev->dev, a38x_comphy_xlate);
+
+       return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id a38x_comphy_of_match_table[] = {
+       { .compatible = "marvell,armada-380-comphy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, a38x_comphy_of_match_table);
+
+static struct platform_driver a38x_comphy_driver = {
+       .probe  = a38x_comphy_probe,
+       .driver = {
+               .name = "armada-38x-comphy",
+               .of_match_table = a38x_comphy_of_match_table,
+       },
+};
+module_platform_driver(a38x_comphy_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
+MODULE_DESCRIPTION("Common PHY driver for Armada 38x SoCs");
+MODULE_LICENSE("GPL v2");
index 05044e3..03ec7a5 100644 (file)
@@ -1513,7 +1513,7 @@ static const struct dmi_system_id chv_no_valid_mask[] = {
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
                        DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_Strago"),
-                       DMI_MATCH(DMI_BOARD_VERSION, "1.0"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
                },
        },
        {
@@ -1521,7 +1521,7 @@ static const struct dmi_system_id chv_no_valid_mask[] = {
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "HP"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"),
-                       DMI_MATCH(DMI_BOARD_VERSION, "1.0"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
                },
        },
        {
@@ -1529,7 +1529,7 @@ static const struct dmi_system_id chv_no_valid_mask[] = {
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Cyan"),
-                       DMI_MATCH(DMI_BOARD_VERSION, "1.0"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
                },
        },
        {
@@ -1537,7 +1537,7 @@ static const struct dmi_system_id chv_no_valid_mask[] = {
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Celes"),
-                       DMI_MATCH(DMI_BOARD_VERSION, "1.0"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
                },
        },
        {}
index 1817786..a005cbc 100644 (file)
@@ -45,12 +45,14 @@ config PINCTRL_MT2701
 config PINCTRL_MT7623
        bool "Mediatek MT7623 pin control with generic binding"
        depends on MACH_MT7623 || COMPILE_TEST
+       depends on OF
        default MACH_MT7623
        select PINCTRL_MTK_MOORE
 
 config PINCTRL_MT7629
        bool "Mediatek MT7629 pin control"
        depends on MACH_MT7629 || COMPILE_TEST
+       depends on OF
        default MACH_MT7629
        select PINCTRL_MTK_MOORE
 
@@ -92,6 +94,7 @@ config PINCTRL_MT6797
 
 config PINCTRL_MT7622
        bool "MediaTek MT7622 pin control"
+       depends on OF
        depends on ARM64 || COMPILE_TEST
        default ARM64 && ARCH_MEDIATEK
        select PINCTRL_MTK_MOORE
index b03481e..98905d4 100644 (file)
@@ -832,8 +832,13 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
                break;
 
        case MCP_TYPE_S18:
+               one_regmap_config =
+                       devm_kmemdup(dev, &mcp23x17_regmap,
+                               sizeof(struct regmap_config), GFP_KERNEL);
+               if (!one_regmap_config)
+                       return -ENOMEM;
                mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp,
-                                              &mcp23x17_regmap);
+                                              one_regmap_config);
                mcp->reg_shift = 1;
                mcp->chip.ngpio = 16;
                mcp->chip.label = "mcp23s18";
index aa8b581..ef4268c 100644 (file)
@@ -588,7 +588,7 @@ static const unsigned int h6_irq_bank_map[] = { 1, 5, 6, 7 };
 static const struct sunxi_pinctrl_desc h6_pinctrl_data = {
        .pins = h6_pins,
        .npins = ARRAY_SIZE(h6_pins),
-       .irq_banks = 3,
+       .irq_banks = 4,
        .irq_bank_map = h6_irq_bank_map,
        .irq_read_needs_mux = true,
 };
index 5d9184d..0e7fa69 100644 (file)
@@ -698,26 +698,24 @@ static int sunxi_pmx_request(struct pinctrl_dev *pctldev, unsigned offset)
 {
        struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
        unsigned short bank = offset / PINS_PER_BANK;
-       struct sunxi_pinctrl_regulator *s_reg = &pctl->regulators[bank];
-       struct regulator *reg;
+       unsigned short bank_offset = bank - pctl->desc->pin_base /
+                                           PINS_PER_BANK;
+       struct sunxi_pinctrl_regulator *s_reg = &pctl->regulators[bank_offset];
+       struct regulator *reg = s_reg->regulator;
+       char supply[16];
        int ret;
 
-       reg = s_reg->regulator;
-       if (!reg) {
-               char supply[16];
-
-               snprintf(supply, sizeof(supply), "vcc-p%c", 'a' + bank);
-               reg = regulator_get(pctl->dev, supply);
-               if (IS_ERR(reg)) {
-                       dev_err(pctl->dev, "Couldn't get bank P%c regulator\n",
-                               'A' + bank);
-                       return PTR_ERR(reg);
-               }
-
-               s_reg->regulator = reg;
-               refcount_set(&s_reg->refcount, 1);
-       } else {
+       if (reg) {
                refcount_inc(&s_reg->refcount);
+               return 0;
+       }
+
+       snprintf(supply, sizeof(supply), "vcc-p%c", 'a' + bank);
+       reg = regulator_get(pctl->dev, supply);
+       if (IS_ERR(reg)) {
+               dev_err(pctl->dev, "Couldn't get bank P%c regulator\n",
+                       'A' + bank);
+               return PTR_ERR(reg);
        }
 
        ret = regulator_enable(reg);
@@ -727,13 +725,13 @@ static int sunxi_pmx_request(struct pinctrl_dev *pctldev, unsigned offset)
                goto out;
        }
 
+       s_reg->regulator = reg;
+       refcount_set(&s_reg->refcount, 1);
+
        return 0;
 
 out:
-       if (refcount_dec_and_test(&s_reg->refcount)) {
-               regulator_put(s_reg->regulator);
-               s_reg->regulator = NULL;
-       }
+       regulator_put(s_reg->regulator);
 
        return ret;
 }
@@ -742,7 +740,9 @@ static int sunxi_pmx_free(struct pinctrl_dev *pctldev, unsigned offset)
 {
        struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
        unsigned short bank = offset / PINS_PER_BANK;
-       struct sunxi_pinctrl_regulator *s_reg = &pctl->regulators[bank];
+       unsigned short bank_offset = bank - pctl->desc->pin_base /
+                                           PINS_PER_BANK;
+       struct sunxi_pinctrl_regulator *s_reg = &pctl->regulators[bank_offset];
 
        if (!refcount_dec_and_test(&s_reg->refcount))
                return 0;
index e340d2a..034c031 100644 (file)
@@ -136,7 +136,7 @@ struct sunxi_pinctrl {
        struct gpio_chip                *chip;
        const struct sunxi_pinctrl_desc *desc;
        struct device                   *dev;
-       struct sunxi_pinctrl_regulator  regulators[12];
+       struct sunxi_pinctrl_regulator  regulators[9];
        struct irq_domain               *domain;
        struct sunxi_pinctrl_function   *functions;
        unsigned                        nfunctions;
index 5e2109c..b5e9db8 100644 (file)
@@ -905,6 +905,7 @@ config TOSHIBA_WMI
 config ACPI_CMPC
        tristate "CMPC Laptop Extras"
        depends on ACPI && INPUT
+       depends on BACKLIGHT_LCD_SUPPORT
        depends on RFKILL || RFKILL=n
        select BACKLIGHT_CLASS_DEVICE
        help
@@ -1128,6 +1129,7 @@ config INTEL_OAKTRAIL
 config SAMSUNG_Q10
        tristate "Samsung Q10 Extras"
        depends on ACPI
+       depends on BACKLIGHT_LCD_SUPPORT
        select BACKLIGHT_CLASS_DEVICE
        ---help---
          This driver provides support for backlight control on Samsung Q10
index aeb4a8b..7fe1863 100644 (file)
@@ -43,7 +43,7 @@ config PTP_1588_CLOCK_DTE
 
 config PTP_1588_CLOCK_QORIQ
        tristate "Freescale QorIQ 1588 timer as PTP clock"
-       depends on GIANFAR || FSL_DPAA_ETH
+       depends on GIANFAR || FSL_DPAA_ETH || FSL_ENETC || FSL_ENETC_VF
        depends on PTP_1588_CLOCK
        default y
        help
index 43416b2..42d3654 100644 (file)
@@ -22,7 +22,6 @@
 
 #include <linux/device.h>
 #include <linux/hrtimer.h>
-#include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
  * Register access functions
  */
 
-/* Caller must hold qoriq_ptp->lock. */
-static u64 tmr_cnt_read(struct qoriq_ptp *qoriq_ptp)
+/* Caller must hold ptp_qoriq->lock. */
+static u64 tmr_cnt_read(struct ptp_qoriq *ptp_qoriq)
 {
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
        u64 ns;
        u32 lo, hi;
 
-       lo = qoriq_read(&regs->ctrl_regs->tmr_cnt_l);
-       hi = qoriq_read(&regs->ctrl_regs->tmr_cnt_h);
+       lo = ptp_qoriq->read(&regs->ctrl_regs->tmr_cnt_l);
+       hi = ptp_qoriq->read(&regs->ctrl_regs->tmr_cnt_h);
        ns = ((u64) hi) << 32;
        ns |= lo;
        return ns;
 }
 
-/* Caller must hold qoriq_ptp->lock. */
-static void tmr_cnt_write(struct qoriq_ptp *qoriq_ptp, u64 ns)
+/* Caller must hold ptp_qoriq->lock. */
+static void tmr_cnt_write(struct ptp_qoriq *ptp_qoriq, u64 ns)
 {
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
        u32 hi = ns >> 32;
        u32 lo = ns & 0xffffffff;
 
-       qoriq_write(&regs->ctrl_regs->tmr_cnt_l, lo);
-       qoriq_write(&regs->ctrl_regs->tmr_cnt_h, hi);
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_cnt_l, lo);
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_cnt_h, hi);
 }
 
-/* Caller must hold qoriq_ptp->lock. */
-static void set_alarm(struct qoriq_ptp *qoriq_ptp)
+/* Caller must hold ptp_qoriq->lock. */
+static void set_alarm(struct ptp_qoriq *ptp_qoriq)
 {
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
        u64 ns;
        u32 lo, hi;
 
-       ns = tmr_cnt_read(qoriq_ptp) + 1500000000ULL;
+       ns = tmr_cnt_read(ptp_qoriq) + 1500000000ULL;
        ns = div_u64(ns, 1000000000UL) * 1000000000ULL;
-       ns -= qoriq_ptp->tclk_period;
+       ns -= ptp_qoriq->tclk_period;
        hi = ns >> 32;
        lo = ns & 0xffffffff;
-       qoriq_write(&regs->alarm_regs->tmr_alarm1_l, lo);
-       qoriq_write(&regs->alarm_regs->tmr_alarm1_h, hi);
+       ptp_qoriq->write(&regs->alarm_regs->tmr_alarm1_l, lo);
+       ptp_qoriq->write(&regs->alarm_regs->tmr_alarm1_h, hi);
 }
 
-/* Caller must hold qoriq_ptp->lock. */
-static void set_fipers(struct qoriq_ptp *qoriq_ptp)
+/* Caller must hold ptp_qoriq->lock. */
+static void set_fipers(struct ptp_qoriq *ptp_qoriq)
 {
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
 
-       set_alarm(qoriq_ptp);
-       qoriq_write(&regs->fiper_regs->tmr_fiper1, qoriq_ptp->tmr_fiper1);
-       qoriq_write(&regs->fiper_regs->tmr_fiper2, qoriq_ptp->tmr_fiper2);
+       set_alarm(ptp_qoriq);
+       ptp_qoriq->write(&regs->fiper_regs->tmr_fiper1, ptp_qoriq->tmr_fiper1);
+       ptp_qoriq->write(&regs->fiper_regs->tmr_fiper2, ptp_qoriq->tmr_fiper2);
 }
 
-static int extts_clean_up(struct qoriq_ptp *qoriq_ptp, int index,
+static int extts_clean_up(struct ptp_qoriq *ptp_qoriq, int index,
                          bool update_event)
 {
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
        struct ptp_clock_event event;
        void __iomem *reg_etts_l;
        void __iomem *reg_etts_h;
@@ -116,17 +115,17 @@ static int extts_clean_up(struct qoriq_ptp *qoriq_ptp, int index,
        event.index = index;
 
        do {
-               lo = qoriq_read(reg_etts_l);
-               hi = qoriq_read(reg_etts_h);
+               lo = ptp_qoriq->read(reg_etts_l);
+               hi = ptp_qoriq->read(reg_etts_h);
 
                if (update_event) {
                        event.timestamp = ((u64) hi) << 32;
                        event.timestamp |= lo;
-                       ptp_clock_event(qoriq_ptp->clock, &event);
+                       ptp_clock_event(ptp_qoriq->clock, &event);
                }
 
-               stat = qoriq_read(&regs->ctrl_regs->tmr_stat);
-       } while (qoriq_ptp->extts_fifo_support && (stat & valid));
+               stat = ptp_qoriq->read(&regs->ctrl_regs->tmr_stat);
+       } while (ptp_qoriq->extts_fifo_support && (stat & valid));
 
        return 0;
 }
@@ -135,89 +134,90 @@ static int extts_clean_up(struct qoriq_ptp *qoriq_ptp, int index,
  * Interrupt service routine
  */
 
-static irqreturn_t isr(int irq, void *priv)
+irqreturn_t ptp_qoriq_isr(int irq, void *priv)
 {
-       struct qoriq_ptp *qoriq_ptp = priv;
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq *ptp_qoriq = priv;
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
        struct ptp_clock_event event;
        u64 ns;
        u32 ack = 0, lo, hi, mask, val, irqs;
 
-       spin_lock(&qoriq_ptp->lock);
+       spin_lock(&ptp_qoriq->lock);
 
-       val = qoriq_read(&regs->ctrl_regs->tmr_tevent);
-       mask = qoriq_read(&regs->ctrl_regs->tmr_temask);
+       val = ptp_qoriq->read(&regs->ctrl_regs->tmr_tevent);
+       mask = ptp_qoriq->read(&regs->ctrl_regs->tmr_temask);
 
-       spin_unlock(&qoriq_ptp->lock);
+       spin_unlock(&ptp_qoriq->lock);
 
        irqs = val & mask;
 
        if (irqs & ETS1) {
                ack |= ETS1;
-               extts_clean_up(qoriq_ptp, 0, true);
+               extts_clean_up(ptp_qoriq, 0, true);
        }
 
        if (irqs & ETS2) {
                ack |= ETS2;
-               extts_clean_up(qoriq_ptp, 1, true);
+               extts_clean_up(ptp_qoriq, 1, true);
        }
 
        if (irqs & ALM2) {
                ack |= ALM2;
-               if (qoriq_ptp->alarm_value) {
+               if (ptp_qoriq->alarm_value) {
                        event.type = PTP_CLOCK_ALARM;
                        event.index = 0;
-                       event.timestamp = qoriq_ptp->alarm_value;
-                       ptp_clock_event(qoriq_ptp->clock, &event);
+                       event.timestamp = ptp_qoriq->alarm_value;
+                       ptp_clock_event(ptp_qoriq->clock, &event);
                }
-               if (qoriq_ptp->alarm_interval) {
-                       ns = qoriq_ptp->alarm_value + qoriq_ptp->alarm_interval;
+               if (ptp_qoriq->alarm_interval) {
+                       ns = ptp_qoriq->alarm_value + ptp_qoriq->alarm_interval;
                        hi = ns >> 32;
                        lo = ns & 0xffffffff;
-                       qoriq_write(&regs->alarm_regs->tmr_alarm2_l, lo);
-                       qoriq_write(&regs->alarm_regs->tmr_alarm2_h, hi);
-                       qoriq_ptp->alarm_value = ns;
+                       ptp_qoriq->write(&regs->alarm_regs->tmr_alarm2_l, lo);
+                       ptp_qoriq->write(&regs->alarm_regs->tmr_alarm2_h, hi);
+                       ptp_qoriq->alarm_value = ns;
                } else {
-                       spin_lock(&qoriq_ptp->lock);
-                       mask = qoriq_read(&regs->ctrl_regs->tmr_temask);
+                       spin_lock(&ptp_qoriq->lock);
+                       mask = ptp_qoriq->read(&regs->ctrl_regs->tmr_temask);
                        mask &= ~ALM2EN;
-                       qoriq_write(&regs->ctrl_regs->tmr_temask, mask);
-                       spin_unlock(&qoriq_ptp->lock);
-                       qoriq_ptp->alarm_value = 0;
-                       qoriq_ptp->alarm_interval = 0;
+                       ptp_qoriq->write(&regs->ctrl_regs->tmr_temask, mask);
+                       spin_unlock(&ptp_qoriq->lock);
+                       ptp_qoriq->alarm_value = 0;
+                       ptp_qoriq->alarm_interval = 0;
                }
        }
 
        if (irqs & PP1) {
                ack |= PP1;
                event.type = PTP_CLOCK_PPS;
-               ptp_clock_event(qoriq_ptp->clock, &event);
+               ptp_clock_event(ptp_qoriq->clock, &event);
        }
 
        if (ack) {
-               qoriq_write(&regs->ctrl_regs->tmr_tevent, ack);
+               ptp_qoriq->write(&regs->ctrl_regs->tmr_tevent, ack);
                return IRQ_HANDLED;
        } else
                return IRQ_NONE;
 }
+EXPORT_SYMBOL_GPL(ptp_qoriq_isr);
 
 /*
  * PTP clock operations
  */
 
-static int ptp_qoriq_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+int ptp_qoriq_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
 {
        u64 adj, diff;
        u32 tmr_add;
        int neg_adj = 0;
-       struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps);
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps);
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
 
        if (scaled_ppm < 0) {
                neg_adj = 1;
                scaled_ppm = -scaled_ppm;
        }
-       tmr_add = qoriq_ptp->tmr_add;
+       tmr_add = ptp_qoriq->tmr_add;
        adj = tmr_add;
 
        /* calculate diff as adj*(scaled_ppm/65536)/1000000
@@ -229,71 +229,74 @@ static int ptp_qoriq_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
 
        tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff;
 
-       qoriq_write(&regs->ctrl_regs->tmr_add, tmr_add);
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_add, tmr_add);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ptp_qoriq_adjfine);
 
-static int ptp_qoriq_adjtime(struct ptp_clock_info *ptp, s64 delta)
+int ptp_qoriq_adjtime(struct ptp_clock_info *ptp, s64 delta)
 {
        s64 now;
        unsigned long flags;
-       struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps);
+       struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps);
 
-       spin_lock_irqsave(&qoriq_ptp->lock, flags);
+       spin_lock_irqsave(&ptp_qoriq->lock, flags);
 
-       now = tmr_cnt_read(qoriq_ptp);
+       now = tmr_cnt_read(ptp_qoriq);
        now += delta;
-       tmr_cnt_write(qoriq_ptp, now);
-       set_fipers(qoriq_ptp);
+       tmr_cnt_write(ptp_qoriq, now);
+       set_fipers(ptp_qoriq);
 
-       spin_unlock_irqrestore(&qoriq_ptp->lock, flags);
+       spin_unlock_irqrestore(&ptp_qoriq->lock, flags);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ptp_qoriq_adjtime);
 
-static int ptp_qoriq_gettime(struct ptp_clock_info *ptp,
-                              struct timespec64 *ts)
+int ptp_qoriq_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
 {
        u64 ns;
        unsigned long flags;
-       struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps);
+       struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps);
 
-       spin_lock_irqsave(&qoriq_ptp->lock, flags);
+       spin_lock_irqsave(&ptp_qoriq->lock, flags);
 
-       ns = tmr_cnt_read(qoriq_ptp);
+       ns = tmr_cnt_read(ptp_qoriq);
 
-       spin_unlock_irqrestore(&qoriq_ptp->lock, flags);
+       spin_unlock_irqrestore(&ptp_qoriq->lock, flags);
 
        *ts = ns_to_timespec64(ns);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ptp_qoriq_gettime);
 
-static int ptp_qoriq_settime(struct ptp_clock_info *ptp,
-                              const struct timespec64 *ts)
+int ptp_qoriq_settime(struct ptp_clock_info *ptp,
+                     const struct timespec64 *ts)
 {
        u64 ns;
        unsigned long flags;
-       struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps);
+       struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps);
 
        ns = timespec64_to_ns(ts);
 
-       spin_lock_irqsave(&qoriq_ptp->lock, flags);
+       spin_lock_irqsave(&ptp_qoriq->lock, flags);
 
-       tmr_cnt_write(qoriq_ptp, ns);
-       set_fipers(qoriq_ptp);
+       tmr_cnt_write(ptp_qoriq, ns);
+       set_fipers(ptp_qoriq);
 
-       spin_unlock_irqrestore(&qoriq_ptp->lock, flags);
+       spin_unlock_irqrestore(&ptp_qoriq->lock, flags);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ptp_qoriq_settime);
 
-static int ptp_qoriq_enable(struct ptp_clock_info *ptp,
-                             struct ptp_clock_request *rq, int on)
+int ptp_qoriq_enable(struct ptp_clock_info *ptp,
+                    struct ptp_clock_request *rq, int on)
 {
-       struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps);
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps);
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
        unsigned long flags;
        u32 bit, mask = 0;
 
@@ -311,7 +314,7 @@ static int ptp_qoriq_enable(struct ptp_clock_info *ptp,
                }
 
                if (on)
-                       extts_clean_up(qoriq_ptp, rq->extts.index, false);
+                       extts_clean_up(ptp_qoriq, rq->extts.index, false);
 
                break;
        case PTP_CLK_REQ_PPS:
@@ -321,21 +324,22 @@ static int ptp_qoriq_enable(struct ptp_clock_info *ptp,
                return -EOPNOTSUPP;
        }
 
-       spin_lock_irqsave(&qoriq_ptp->lock, flags);
+       spin_lock_irqsave(&ptp_qoriq->lock, flags);
 
-       mask = qoriq_read(&regs->ctrl_regs->tmr_temask);
+       mask = ptp_qoriq->read(&regs->ctrl_regs->tmr_temask);
        if (on) {
                mask |= bit;
-               qoriq_write(&regs->ctrl_regs->tmr_tevent, bit);
+               ptp_qoriq->write(&regs->ctrl_regs->tmr_tevent, bit);
        } else {
                mask &= ~bit;
        }
 
-       qoriq_write(&regs->ctrl_regs->tmr_temask, mask);
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_temask, mask);
 
-       spin_unlock_irqrestore(&qoriq_ptp->lock, flags);
+       spin_unlock_irqrestore(&ptp_qoriq->lock, flags);
        return 0;
 }
+EXPORT_SYMBOL_GPL(ptp_qoriq_enable);
 
 static const struct ptp_clock_info ptp_qoriq_caps = {
        .owner          = THIS_MODULE,
@@ -354,7 +358,7 @@ static const struct ptp_clock_info ptp_qoriq_caps = {
 };
 
 /**
- * qoriq_ptp_nominal_freq - calculate nominal frequency according to
+ * ptp_qoriq_nominal_freq - calculate nominal frequency according to
  *                         reference clock frequency
  *
  * @clk_src: reference clock frequency
@@ -365,7 +369,7 @@ static const struct ptp_clock_info ptp_qoriq_caps = {
  *
  * Return the nominal frequency
  */
-static u32 qoriq_ptp_nominal_freq(u32 clk_src)
+static u32 ptp_qoriq_nominal_freq(u32 clk_src)
 {
        u32 remainder = 0;
 
@@ -385,9 +389,9 @@ static u32 qoriq_ptp_nominal_freq(u32 clk_src)
 }
 
 /**
- * qoriq_ptp_auto_config - calculate a set of default configurations
+ * ptp_qoriq_auto_config - calculate a set of default configurations
  *
- * @qoriq_ptp: pointer to qoriq_ptp
+ * @ptp_qoriq: pointer to ptp_qoriq
  * @node: pointer to device_node
  *
  * If below dts properties are not provided, this function will be
@@ -401,7 +405,7 @@ static u32 qoriq_ptp_nominal_freq(u32 clk_src)
  *
  * Return 0 if success
  */
-static int qoriq_ptp_auto_config(struct qoriq_ptp *qoriq_ptp,
+static int ptp_qoriq_auto_config(struct ptp_qoriq *ptp_qoriq,
                                 struct device_node *node)
 {
        struct clk *clk;
@@ -411,7 +415,7 @@ static int qoriq_ptp_auto_config(struct qoriq_ptp *qoriq_ptp,
        u32 remainder = 0;
        u32 clk_src = 0;
 
-       qoriq_ptp->cksel = DEFAULT_CKSEL;
+       ptp_qoriq->cksel = DEFAULT_CKSEL;
 
        clk = of_clk_get(node, 0);
        if (!IS_ERR(clk)) {
@@ -424,12 +428,12 @@ static int qoriq_ptp_auto_config(struct qoriq_ptp *qoriq_ptp,
                return -EINVAL;
        }
 
-       nominal_freq = qoriq_ptp_nominal_freq(clk_src);
+       nominal_freq = ptp_qoriq_nominal_freq(clk_src);
        if (!nominal_freq)
                return -EINVAL;
 
-       qoriq_ptp->tclk_period = 1000000000UL / nominal_freq;
-       qoriq_ptp->tmr_prsc = DEFAULT_TMR_PRSC;
+       ptp_qoriq->tclk_period = 1000000000UL / nominal_freq;
+       ptp_qoriq->tmr_prsc = DEFAULT_TMR_PRSC;
 
        /* Calculate initial frequency compensation value for TMR_ADD register.
         * freq_comp = ceil(2^32 / freq_ratio)
@@ -440,172 +444,193 @@ static int qoriq_ptp_auto_config(struct qoriq_ptp *qoriq_ptp,
        if (remainder)
                freq_comp++;
 
-       qoriq_ptp->tmr_add = freq_comp;
-       qoriq_ptp->tmr_fiper1 = DEFAULT_FIPER1_PERIOD - qoriq_ptp->tclk_period;
-       qoriq_ptp->tmr_fiper2 = DEFAULT_FIPER2_PERIOD - qoriq_ptp->tclk_period;
+       ptp_qoriq->tmr_add = freq_comp;
+       ptp_qoriq->tmr_fiper1 = DEFAULT_FIPER1_PERIOD - ptp_qoriq->tclk_period;
+       ptp_qoriq->tmr_fiper2 = DEFAULT_FIPER2_PERIOD - ptp_qoriq->tclk_period;
 
        /* max_adj = 1000000000 * (freq_ratio - 1.0) - 1
         * freq_ratio = reference_clock_freq / nominal_freq
         */
        max_adj = 1000000000ULL * (clk_src - nominal_freq);
        max_adj = div_u64(max_adj, nominal_freq) - 1;
-       qoriq_ptp->caps.max_adj = max_adj;
+       ptp_qoriq->caps.max_adj = max_adj;
 
        return 0;
 }
 
-static int qoriq_ptp_probe(struct platform_device *dev)
+int ptp_qoriq_init(struct ptp_qoriq *ptp_qoriq, void __iomem *base,
+                  const struct ptp_clock_info caps)
 {
-       struct device_node *node = dev->dev.of_node;
-       struct qoriq_ptp *qoriq_ptp;
-       struct qoriq_ptp_registers *regs;
+       struct device_node *node = ptp_qoriq->dev->of_node;
+       struct ptp_qoriq_registers *regs;
        struct timespec64 now;
-       int err = -ENOMEM;
-       u32 tmr_ctrl;
        unsigned long flags;
-       void __iomem *base;
-
-       qoriq_ptp = kzalloc(sizeof(*qoriq_ptp), GFP_KERNEL);
-       if (!qoriq_ptp)
-               goto no_memory;
-
-       err = -EINVAL;
+       u32 tmr_ctrl;
 
-       qoriq_ptp->dev = &dev->dev;
-       qoriq_ptp->caps = ptp_qoriq_caps;
+       ptp_qoriq->base = base;
+       ptp_qoriq->caps = caps;
 
-       if (of_property_read_u32(node, "fsl,cksel", &qoriq_ptp->cksel))
-               qoriq_ptp->cksel = DEFAULT_CKSEL;
+       if (of_property_read_u32(node, "fsl,cksel", &ptp_qoriq->cksel))
+               ptp_qoriq->cksel = DEFAULT_CKSEL;
 
        if (of_property_read_bool(node, "fsl,extts-fifo"))
-               qoriq_ptp->extts_fifo_support = true;
+               ptp_qoriq->extts_fifo_support = true;
        else
-               qoriq_ptp->extts_fifo_support = false;
+               ptp_qoriq->extts_fifo_support = false;
 
        if (of_property_read_u32(node,
-                                "fsl,tclk-period", &qoriq_ptp->tclk_period) ||
+                                "fsl,tclk-period", &ptp_qoriq->tclk_period) ||
            of_property_read_u32(node,
-                                "fsl,tmr-prsc", &qoriq_ptp->tmr_prsc) ||
+                                "fsl,tmr-prsc", &ptp_qoriq->tmr_prsc) ||
            of_property_read_u32(node,
-                                "fsl,tmr-add", &qoriq_ptp->tmr_add) ||
+                                "fsl,tmr-add", &ptp_qoriq->tmr_add) ||
            of_property_read_u32(node,
-                                "fsl,tmr-fiper1", &qoriq_ptp->tmr_fiper1) ||
+                                "fsl,tmr-fiper1", &ptp_qoriq->tmr_fiper1) ||
            of_property_read_u32(node,
-                                "fsl,tmr-fiper2", &qoriq_ptp->tmr_fiper2) ||
+                                "fsl,tmr-fiper2", &ptp_qoriq->tmr_fiper2) ||
            of_property_read_u32(node,
-                                "fsl,max-adj", &qoriq_ptp->caps.max_adj)) {
+                                "fsl,max-adj", &ptp_qoriq->caps.max_adj)) {
                pr_warn("device tree node missing required elements, try automatic configuration\n");
 
-               if (qoriq_ptp_auto_config(qoriq_ptp, node))
-                       goto no_config;
+               if (ptp_qoriq_auto_config(ptp_qoriq, node))
+                       return -ENODEV;
        }
 
-       err = -ENODEV;
+       if (of_property_read_bool(node, "little-endian")) {
+               ptp_qoriq->read = qoriq_read_le;
+               ptp_qoriq->write = qoriq_write_le;
+       } else {
+               ptp_qoriq->read = qoriq_read_be;
+               ptp_qoriq->write = qoriq_write_be;
+       }
+
+       /* The eTSEC uses differnt memory map with DPAA/ENETC */
+       if (of_device_is_compatible(node, "fsl,etsec-ptp")) {
+               ptp_qoriq->regs.ctrl_regs = base + ETSEC_CTRL_REGS_OFFSET;
+               ptp_qoriq->regs.alarm_regs = base + ETSEC_ALARM_REGS_OFFSET;
+               ptp_qoriq->regs.fiper_regs = base + ETSEC_FIPER_REGS_OFFSET;
+               ptp_qoriq->regs.etts_regs = base + ETSEC_ETTS_REGS_OFFSET;
+       } else {
+               ptp_qoriq->regs.ctrl_regs = base + CTRL_REGS_OFFSET;
+               ptp_qoriq->regs.alarm_regs = base + ALARM_REGS_OFFSET;
+               ptp_qoriq->regs.fiper_regs = base + FIPER_REGS_OFFSET;
+               ptp_qoriq->regs.etts_regs = base + ETTS_REGS_OFFSET;
+       }
+
+       ktime_get_real_ts64(&now);
+       ptp_qoriq_settime(&ptp_qoriq->caps, &now);
+
+       tmr_ctrl =
+         (ptp_qoriq->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT |
+         (ptp_qoriq->cksel & CKSEL_MASK) << CKSEL_SHIFT;
+
+       spin_lock_init(&ptp_qoriq->lock);
+       spin_lock_irqsave(&ptp_qoriq->lock, flags);
+
+       regs = &ptp_qoriq->regs;
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_ctrl, tmr_ctrl);
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_add, ptp_qoriq->tmr_add);
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_prsc, ptp_qoriq->tmr_prsc);
+       ptp_qoriq->write(&regs->fiper_regs->tmr_fiper1, ptp_qoriq->tmr_fiper1);
+       ptp_qoriq->write(&regs->fiper_regs->tmr_fiper2, ptp_qoriq->tmr_fiper2);
+       set_alarm(ptp_qoriq);
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_ctrl,
+                        tmr_ctrl|FIPERST|RTPE|TE|FRD);
+
+       spin_unlock_irqrestore(&ptp_qoriq->lock, flags);
+
+       ptp_qoriq->clock = ptp_clock_register(&ptp_qoriq->caps, ptp_qoriq->dev);
+       if (IS_ERR(ptp_qoriq->clock))
+               return PTR_ERR(ptp_qoriq->clock);
+
+       ptp_qoriq->phc_index = ptp_clock_index(ptp_qoriq->clock);
+       ptp_qoriq_create_debugfs(ptp_qoriq);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ptp_qoriq_init);
+
+void ptp_qoriq_free(struct ptp_qoriq *ptp_qoriq)
+{
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
+
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_temask, 0);
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_ctrl,   0);
+
+       ptp_qoriq_remove_debugfs(ptp_qoriq);
+       ptp_clock_unregister(ptp_qoriq->clock);
+       iounmap(ptp_qoriq->base);
+       free_irq(ptp_qoriq->irq, ptp_qoriq);
+}
+EXPORT_SYMBOL_GPL(ptp_qoriq_free);
 
-       qoriq_ptp->irq = platform_get_irq(dev, 0);
+static int ptp_qoriq_probe(struct platform_device *dev)
+{
+       struct ptp_qoriq *ptp_qoriq;
+       int err = -ENOMEM;
+       void __iomem *base;
+
+       ptp_qoriq = kzalloc(sizeof(*ptp_qoriq), GFP_KERNEL);
+       if (!ptp_qoriq)
+               goto no_memory;
+
+       ptp_qoriq->dev = &dev->dev;
 
-       if (qoriq_ptp->irq < 0) {
+       err = -ENODEV;
+
+       ptp_qoriq->irq = platform_get_irq(dev, 0);
+       if (ptp_qoriq->irq < 0) {
                pr_err("irq not in device tree\n");
                goto no_node;
        }
-       if (request_irq(qoriq_ptp->irq, isr, IRQF_SHARED, DRIVER, qoriq_ptp)) {
+       if (request_irq(ptp_qoriq->irq, ptp_qoriq_isr, IRQF_SHARED,
+                       DRIVER, ptp_qoriq)) {
                pr_err("request_irq failed\n");
                goto no_node;
        }
 
-       qoriq_ptp->rsrc = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       if (!qoriq_ptp->rsrc) {
+       ptp_qoriq->rsrc = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       if (!ptp_qoriq->rsrc) {
                pr_err("no resource\n");
                goto no_resource;
        }
-       if (request_resource(&iomem_resource, qoriq_ptp->rsrc)) {
+       if (request_resource(&iomem_resource, ptp_qoriq->rsrc)) {
                pr_err("resource busy\n");
                goto no_resource;
        }
 
-       spin_lock_init(&qoriq_ptp->lock);
-
-       base = ioremap(qoriq_ptp->rsrc->start,
-                      resource_size(qoriq_ptp->rsrc));
+       base = ioremap(ptp_qoriq->rsrc->start,
+                      resource_size(ptp_qoriq->rsrc));
        if (!base) {
                pr_err("ioremap ptp registers failed\n");
                goto no_ioremap;
        }
 
-       qoriq_ptp->base = base;
-
-       if (of_device_is_compatible(node, "fsl,fman-ptp-timer")) {
-               qoriq_ptp->regs.ctrl_regs = base + FMAN_CTRL_REGS_OFFSET;
-               qoriq_ptp->regs.alarm_regs = base + FMAN_ALARM_REGS_OFFSET;
-               qoriq_ptp->regs.fiper_regs = base + FMAN_FIPER_REGS_OFFSET;
-               qoriq_ptp->regs.etts_regs = base + FMAN_ETTS_REGS_OFFSET;
-       } else {
-               qoriq_ptp->regs.ctrl_regs = base + CTRL_REGS_OFFSET;
-               qoriq_ptp->regs.alarm_regs = base + ALARM_REGS_OFFSET;
-               qoriq_ptp->regs.fiper_regs = base + FIPER_REGS_OFFSET;
-               qoriq_ptp->regs.etts_regs = base + ETTS_REGS_OFFSET;
-       }
-
-       ktime_get_real_ts64(&now);
-       ptp_qoriq_settime(&qoriq_ptp->caps, &now);
-
-       tmr_ctrl =
-         (qoriq_ptp->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT |
-         (qoriq_ptp->cksel & CKSEL_MASK) << CKSEL_SHIFT;
-
-       spin_lock_irqsave(&qoriq_ptp->lock, flags);
-
-       regs = &qoriq_ptp->regs;
-       qoriq_write(&regs->ctrl_regs->tmr_ctrl,   tmr_ctrl);
-       qoriq_write(&regs->ctrl_regs->tmr_add,    qoriq_ptp->tmr_add);
-       qoriq_write(&regs->ctrl_regs->tmr_prsc,   qoriq_ptp->tmr_prsc);
-       qoriq_write(&regs->fiper_regs->tmr_fiper1, qoriq_ptp->tmr_fiper1);
-       qoriq_write(&regs->fiper_regs->tmr_fiper2, qoriq_ptp->tmr_fiper2);
-       set_alarm(qoriq_ptp);
-       qoriq_write(&regs->ctrl_regs->tmr_ctrl,   tmr_ctrl|FIPERST|RTPE|TE|FRD);
-
-       spin_unlock_irqrestore(&qoriq_ptp->lock, flags);
-
-       qoriq_ptp->clock = ptp_clock_register(&qoriq_ptp->caps, &dev->dev);
-       if (IS_ERR(qoriq_ptp->clock)) {
-               err = PTR_ERR(qoriq_ptp->clock);
+       err = ptp_qoriq_init(ptp_qoriq, base, ptp_qoriq_caps);
+       if (err)
                goto no_clock;
-       }
-       qoriq_ptp->phc_index = ptp_clock_index(qoriq_ptp->clock);
-
-       ptp_qoriq_create_debugfs(qoriq_ptp);
-       platform_set_drvdata(dev, qoriq_ptp);
 
+       platform_set_drvdata(dev, ptp_qoriq);
        return 0;
 
 no_clock:
-       iounmap(qoriq_ptp->base);
+       iounmap(ptp_qoriq->base);
 no_ioremap:
-       release_resource(qoriq_ptp->rsrc);
+       release_resource(ptp_qoriq->rsrc);
 no_resource:
-       free_irq(qoriq_ptp->irq, qoriq_ptp);
-no_config:
+       free_irq(ptp_qoriq->irq, ptp_qoriq);
 no_node:
-       kfree(qoriq_ptp);
+       kfree(ptp_qoriq);
 no_memory:
        return err;
 }
 
-static int qoriq_ptp_remove(struct platform_device *dev)
+static int ptp_qoriq_remove(struct platform_device *dev)
 {
-       struct qoriq_ptp *qoriq_ptp = platform_get_drvdata(dev);
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
-
-       qoriq_write(&regs->ctrl_regs->tmr_temask, 0);
-       qoriq_write(&regs->ctrl_regs->tmr_ctrl,   0);
-
-       ptp_qoriq_remove_debugfs(qoriq_ptp);
-       ptp_clock_unregister(qoriq_ptp->clock);
-       iounmap(qoriq_ptp->base);
-       release_resource(qoriq_ptp->rsrc);
-       free_irq(qoriq_ptp->irq, qoriq_ptp);
-       kfree(qoriq_ptp);
+       struct ptp_qoriq *ptp_qoriq = platform_get_drvdata(dev);
 
+       ptp_qoriq_free(ptp_qoriq);
+       release_resource(ptp_qoriq->rsrc);
+       kfree(ptp_qoriq);
        return 0;
 }
 
@@ -616,16 +641,16 @@ static const struct of_device_id match_table[] = {
 };
 MODULE_DEVICE_TABLE(of, match_table);
 
-static struct platform_driver qoriq_ptp_driver = {
+static struct platform_driver ptp_qoriq_driver = {
        .driver = {
                .name           = "ptp_qoriq",
                .of_match_table = match_table,
        },
-       .probe       = qoriq_ptp_probe,
-       .remove      = qoriq_ptp_remove,
+       .probe       = ptp_qoriq_probe,
+       .remove      = ptp_qoriq_remove,
 };
 
-module_platform_driver(qoriq_ptp_driver);
+module_platform_driver(ptp_qoriq_driver);
 
 MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
 MODULE_DESCRIPTION("PTP clock for Freescale QorIQ 1588 timer");
index 9705950..e8dddce 100644 (file)
@@ -7,11 +7,11 @@
 
 static int ptp_qoriq_fiper1_lpbk_get(void *data, u64 *val)
 {
-       struct qoriq_ptp *qoriq_ptp = data;
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq *ptp_qoriq = data;
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
        u32 ctrl;
 
-       ctrl = qoriq_read(&regs->ctrl_regs->tmr_ctrl);
+       ctrl = ptp_qoriq->read(&regs->ctrl_regs->tmr_ctrl);
        *val = ctrl & PP1L ? 1 : 0;
 
        return 0;
@@ -19,17 +19,17 @@ static int ptp_qoriq_fiper1_lpbk_get(void *data, u64 *val)
 
 static int ptp_qoriq_fiper1_lpbk_set(void *data, u64 val)
 {
-       struct qoriq_ptp *qoriq_ptp = data;
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq *ptp_qoriq = data;
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
        u32 ctrl;
 
-       ctrl = qoriq_read(&regs->ctrl_regs->tmr_ctrl);
+       ctrl = ptp_qoriq->read(&regs->ctrl_regs->tmr_ctrl);
        if (val == 0)
                ctrl &= ~PP1L;
        else
                ctrl |= PP1L;
 
-       qoriq_write(&regs->ctrl_regs->tmr_ctrl, ctrl);
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_ctrl, ctrl);
        return 0;
 }
 
@@ -38,11 +38,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(ptp_qoriq_fiper1_fops, ptp_qoriq_fiper1_lpbk_get,
 
 static int ptp_qoriq_fiper2_lpbk_get(void *data, u64 *val)
 {
-       struct qoriq_ptp *qoriq_ptp = data;
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq *ptp_qoriq = data;
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
        u32 ctrl;
 
-       ctrl = qoriq_read(&regs->ctrl_regs->tmr_ctrl);
+       ctrl = ptp_qoriq->read(&regs->ctrl_regs->tmr_ctrl);
        *val = ctrl & PP2L ? 1 : 0;
 
        return 0;
@@ -50,52 +50,52 @@ static int ptp_qoriq_fiper2_lpbk_get(void *data, u64 *val)
 
 static int ptp_qoriq_fiper2_lpbk_set(void *data, u64 val)
 {
-       struct qoriq_ptp *qoriq_ptp = data;
-       struct qoriq_ptp_registers *regs = &qoriq_ptp->regs;
+       struct ptp_qoriq *ptp_qoriq = data;
+       struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
        u32 ctrl;
 
-       ctrl = qoriq_read(&regs->ctrl_regs->tmr_ctrl);
+       ctrl = ptp_qoriq->read(&regs->ctrl_regs->tmr_ctrl);
        if (val == 0)
                ctrl &= ~PP2L;
        else
                ctrl |= PP2L;
 
-       qoriq_write(&regs->ctrl_regs->tmr_ctrl, ctrl);
+       ptp_qoriq->write(&regs->ctrl_regs->tmr_ctrl, ctrl);
        return 0;
 }
 
 DEFINE_DEBUGFS_ATTRIBUTE(ptp_qoriq_fiper2_fops, ptp_qoriq_fiper2_lpbk_get,
                         ptp_qoriq_fiper2_lpbk_set, "%llu\n");
 
-void ptp_qoriq_create_debugfs(struct qoriq_ptp *qoriq_ptp)
+void ptp_qoriq_create_debugfs(struct ptp_qoriq *ptp_qoriq)
 {
        struct dentry *root;
 
-       root = debugfs_create_dir(dev_name(qoriq_ptp->dev), NULL);
+       root = debugfs_create_dir(dev_name(ptp_qoriq->dev), NULL);
        if (IS_ERR(root))
                return;
        if (!root)
                goto err_root;
 
-       qoriq_ptp->debugfs_root = root;
+       ptp_qoriq->debugfs_root = root;
 
        if (!debugfs_create_file_unsafe("fiper1-loopback", 0600, root,
-                                       qoriq_ptp, &ptp_qoriq_fiper1_fops))
+                                       ptp_qoriq, &ptp_qoriq_fiper1_fops))
                goto err_node;
        if (!debugfs_create_file_unsafe("fiper2-loopback", 0600, root,
-                                       qoriq_ptp, &ptp_qoriq_fiper2_fops))
+                                       ptp_qoriq, &ptp_qoriq_fiper2_fops))
                goto err_node;
        return;
 
 err_node:
        debugfs_remove_recursive(root);
-       qoriq_ptp->debugfs_root = NULL;
+       ptp_qoriq->debugfs_root = NULL;
 err_root:
-       dev_err(qoriq_ptp->dev, "failed to initialize debugfs\n");
+       dev_err(ptp_qoriq->dev, "failed to initialize debugfs\n");
 }
 
-void ptp_qoriq_remove_debugfs(struct qoriq_ptp *qoriq_ptp)
+void ptp_qoriq_remove_debugfs(struct ptp_qoriq *ptp_qoriq)
 {
-       debugfs_remove_recursive(qoriq_ptp->debugfs_root);
-       qoriq_ptp->debugfs_root = NULL;
+       debugfs_remove_recursive(ptp_qoriq->debugfs_root);
+       ptp_qoriq->debugfs_root = NULL;
 }
index 4e7b55a..6e294b4 100644 (file)
@@ -4469,6 +4469,14 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp)
                usrparm.psf_data &= 0x7fffffffULL;
                usrparm.rssd_result &= 0x7fffffffULL;
        }
+       /* at least 2 bytes are accessed and should be allocated */
+       if (usrparm.psf_data_len < 2) {
+               DBF_DEV_EVENT(DBF_WARNING, device,
+                             "Symmetrix ioctl invalid data length %d",
+                             usrparm.psf_data_len);
+               rc = -EINVAL;
+               goto out;
+       }
        /* alloc I/O data area */
        psf_data = kzalloc(usrparm.psf_data_len, GFP_KERNEL | GFP_DMA);
        rssd_result = kzalloc(usrparm.rssd_result_len, GFP_KERNEL | GFP_DMA);
index 48ea000..5a69974 100644 (file)
@@ -248,7 +248,8 @@ static inline int ap_test_config(unsigned int *field, unsigned int nr)
 static inline int ap_test_config_card_id(unsigned int id)
 {
        if (!ap_configuration)  /* QCI not supported */
-               return 1;
+               /* only ids 0...3F may be probed */
+               return id < 0x40 ? 1 : 0;
        return ap_test_config(ap_configuration->apm, id);
 }
 
index f2d6bbe..bc55ec3 100644 (file)
@@ -9,7 +9,7 @@ obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o
 obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
 obj-$(CONFIG_SMSGIUCV_EVENT) += smsgiucv_app.o
 obj-$(CONFIG_LCS) += lcs.o
-qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o
+qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o qeth_ethtool.o
 obj-$(CONFIG_QETH) += qeth.o
 qeth_l2-y += qeth_l2_main.o qeth_l2_sys.o
 obj-$(CONFIG_QETH_L2) += qeth_l2.o
index d65650e..c0c46be 100644 (file)
 #include <linux/in6.h>
 #include <linux/bitops.h>
 #include <linux/seq_file.h>
-#include <linux/ethtool.h>
 #include <linux/hashtable.h>
 #include <linux/ip.h>
 #include <linux/refcount.h>
+#include <linux/workqueue.h>
 
 #include <net/ipv6.h>
 #include <net/if_inet6.h>
@@ -34,6 +34,8 @@
 #include <asm/ccwgroup.h>
 #include <asm/sysinfo.h>
 
+#include <uapi/linux/if_link.h>
+
 #include "qeth_core_mpc.h"
 
 /**
@@ -112,59 +114,6 @@ static inline u32 qeth_get_device_id(struct ccw_device *cdev)
 #define CCW_DEVID(cdev)                (qeth_get_device_id(cdev))
 #define CARD_DEVID(card)       (CCW_DEVID(CARD_RDEV(card)))
 
-/**
- * card stuff
- */
-struct qeth_perf_stats {
-       unsigned int bufs_rec;
-       unsigned int bufs_sent;
-       unsigned int buf_elements_sent;
-
-       unsigned int skbs_sent_pack;
-       unsigned int bufs_sent_pack;
-
-       unsigned int sc_dp_p;
-       unsigned int sc_p_dp;
-       /* qdio_cq_handler: number of times called, time spent in */
-       __u64 cq_start_time;
-       unsigned int cq_cnt;
-       unsigned int cq_time;
-       /* qdio_input_handler: number of times called, time spent in */
-       __u64 inbound_start_time;
-       unsigned int inbound_cnt;
-       unsigned int inbound_time;
-       /* qeth_send_packet: number of times called, time spent in */
-       __u64 outbound_start_time;
-       unsigned int outbound_cnt;
-       unsigned int outbound_time;
-       /* qdio_output_handler: number of times called, time spent in */
-       __u64 outbound_handler_start_time;
-       unsigned int outbound_handler_cnt;
-       unsigned int outbound_handler_time;
-       /* number of calls to and time spent in do_QDIO for inbound queue */
-       __u64 inbound_do_qdio_start_time;
-       unsigned int inbound_do_qdio_cnt;
-       unsigned int inbound_do_qdio_time;
-       /* number of calls to and time spent in do_QDIO for outbound queues */
-       __u64 outbound_do_qdio_start_time;
-       unsigned int outbound_do_qdio_cnt;
-       unsigned int outbound_do_qdio_time;
-       unsigned int large_send_bytes;
-       unsigned int large_send_cnt;
-       unsigned int sg_skbs_sent;
-       /* initial values when measuring starts */
-       unsigned long initial_rx_packets;
-       unsigned long initial_tx_packets;
-       /* inbound scatter gather data */
-       unsigned int sg_skbs_rx;
-       unsigned int sg_frags_rx;
-       unsigned int sg_alloc_page_rx;
-       unsigned int tx_csum;
-       unsigned int tx_lin;
-       unsigned int tx_linfail;
-       unsigned int rx_csum;
-};
-
 /* Routing stuff */
 struct qeth_routing_info {
        enum qeth_routing_types type;
@@ -494,10 +443,54 @@ enum qeth_out_q_states {
        QETH_OUT_Q_LOCKED_FLUSH,
 };
 
+#define QETH_CARD_STAT_ADD(_c, _stat, _val)    ((_c)->stats._stat += (_val))
+#define QETH_CARD_STAT_INC(_c, _stat)          QETH_CARD_STAT_ADD(_c, _stat, 1)
+
+#define QETH_TXQ_STAT_ADD(_q, _stat, _val)     ((_q)->stats._stat += (_val))
+#define QETH_TXQ_STAT_INC(_q, _stat)           QETH_TXQ_STAT_ADD(_q, _stat, 1)
+
+struct qeth_card_stats {
+       u64 rx_bufs;
+       u64 rx_skb_csum;
+       u64 rx_sg_skbs;
+       u64 rx_sg_frags;
+       u64 rx_sg_alloc_page;
+
+       /* rtnl_link_stats64 */
+       u64 rx_packets;
+       u64 rx_bytes;
+       u64 rx_errors;
+       u64 rx_dropped;
+       u64 rx_multicast;
+       u64 tx_errors;
+};
+
+struct qeth_out_q_stats {
+       u64 bufs;
+       u64 bufs_pack;
+       u64 buf_elements;
+       u64 skbs_pack;
+       u64 skbs_sg;
+       u64 skbs_csum;
+       u64 skbs_tso;
+       u64 skbs_linearized;
+       u64 skbs_linearized_fail;
+       u64 tso_bytes;
+       u64 packing_mode_switch;
+
+       /* rtnl_link_stats64 */
+       u64 tx_packets;
+       u64 tx_bytes;
+       u64 tx_errors;
+       u64 tx_dropped;
+       u64 tx_carrier_errors;
+};
+
 struct qeth_qdio_out_q {
        struct qdio_buffer *qdio_bufs[QDIO_MAX_BUFFERS_PER_Q];
        struct qeth_qdio_out_buffer *bufs[QDIO_MAX_BUFFERS_PER_Q];
        struct qdio_outbuf_state *bufstates; /* convenience pointer */
+       struct qeth_out_q_stats stats;
        int queue_no;
        struct qeth_card *card;
        atomic_t state;
@@ -527,7 +520,7 @@ struct qeth_qdio_info {
 
        /* output */
        int no_out_queues;
-       struct qeth_qdio_out_q **out_qs;
+       struct qeth_qdio_out_q *out_qs[QETH_MAX_QUEUES];
        struct qdio_outbuf_state *out_bufstates;
 
        /* priority queueing */
@@ -594,8 +587,8 @@ struct qeth_channel;
 struct qeth_cmd_buffer {
        enum qeth_cmd_buffer_state state;
        struct qeth_channel *channel;
+       struct qeth_reply *reply;
        unsigned char *data;
-       int rc;
        void (*callback)(struct qeth_card *card, struct qeth_channel *channel,
                         struct qeth_cmd_buffer *iob);
 };
@@ -701,7 +694,6 @@ struct qeth_card_options {
        struct qeth_vnicc_info vnicc; /* VNICC options */
        int fake_broadcast;
        enum qeth_discipline_id layer;
-       int performance_stats;
        int rx_sg_cb;
        enum qeth_ipa_isolation_modes isolation;
        enum qeth_ipa_isolation_modes prev_isolation;
@@ -777,13 +769,13 @@ struct qeth_card {
        struct qeth_channel data;
 
        struct net_device *dev;
-       struct net_device_stats stats;
-
+       struct qeth_card_stats stats;
        struct qeth_card_info info;
        struct qeth_token token;
        struct qeth_seqno seqno;
        struct qeth_card_options options;
 
+       struct workqueue_struct *event_wq;
        wait_queue_head_t wait_q;
        spinlock_t mclock;
        unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
@@ -800,7 +792,6 @@ struct qeth_card {
        struct list_head cmd_waiter_list;
        /* QDIO buffer handling */
        struct qeth_qdio_info qdio;
-       struct qeth_perf_stats perf_stats;
        int read_or_write_problem;
        struct qeth_osn_info osn_info;
        struct qeth_discipline *discipline;
@@ -857,11 +848,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_micros(void)
-{
-       return (int) (get_tod_clock() >> 12);
-}
-
 static inline int qeth_get_ip_version(struct sk_buff *skb)
 {
        struct vlan_ethhdr *veth = vlan_eth_hdr(skb);
@@ -886,8 +872,7 @@ static inline void qeth_rx_csum(struct qeth_card *card, struct sk_buff *skb,
        if ((card->dev->features & NETIF_F_RXCSUM) &&
            (flags & QETH_HDR_EXT_CSUM_TRANSP_REQ)) {
                skb->ip_summed = CHECKSUM_UNNECESSARY;
-               if (card->options.performance_stats)
-                       card->perf_stats.rx_csum++;
+               QETH_CARD_STAT_INC(card, rx_skb_csum);
        } else {
                skb->ip_summed = CHECKSUM_NONE;
        }
@@ -949,12 +934,13 @@ static inline struct qeth_qdio_out_q *qeth_get_tx_queue(struct qeth_card *card,
 
 extern struct qeth_discipline qeth_l2_discipline;
 extern struct qeth_discipline qeth_l3_discipline;
+extern const struct ethtool_ops qeth_ethtool_ops;
+extern const struct ethtool_ops qeth_osn_ethtool_ops;
 extern const struct attribute_group *qeth_generic_attr_groups[];
 extern const struct attribute_group *qeth_osn_attr_groups[];
 extern const struct attribute_group qeth_device_attr_group;
 extern const struct attribute_group qeth_device_blkt_group;
 extern const struct device_type qeth_generic_devtype;
-extern struct workqueue_struct *qeth_wq;
 
 int qeth_card_hw_is_reachable(struct qeth_card *);
 const char *qeth_get_cardname_short(struct qeth_card *);
@@ -993,33 +979,25 @@ void qeth_clear_working_pool_list(struct qeth_card *);
 void qeth_clear_cmd_buffers(struct qeth_channel *);
 void qeth_clear_qdio_buffers(struct qeth_card *);
 void qeth_setadp_promisc_mode(struct qeth_card *);
-struct net_device_stats *qeth_get_stats(struct net_device *);
 int qeth_setadpparms_change_macaddr(struct qeth_card *);
 void qeth_tx_timeout(struct net_device *);
 void qeth_prepare_control_data(struct qeth_card *, int,
                                struct qeth_cmd_buffer *);
 void qeth_release_buffer(struct qeth_channel *, struct qeth_cmd_buffer *);
-void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob);
+void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+                         u16 cmd_length);
 struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *);
 int qeth_query_switch_attributes(struct qeth_card *card,
                                  struct qeth_switch_info *sw_info);
-int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,
-       int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long),
-       void *reply_param);
+int qeth_query_card_info(struct qeth_card *card,
+                        struct carrier_info *carrier_info);
 unsigned int qeth_count_elements(struct sk_buff *skb, unsigned int data_offset);
 int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                        struct sk_buff *skb, struct qeth_hdr *hdr,
                        unsigned int offset, unsigned int hd_len,
                        int elements_needed);
 int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-int qeth_core_get_sset_count(struct net_device *, int);
-void qeth_core_get_ethtool_stats(struct net_device *,
-                               struct ethtool_stats *, u64 *);
-void qeth_core_get_strings(struct net_device *, u32, u8 *);
-void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
 void qeth_dbf_longtext(debug_info_t *id, int level, char *text, ...);
-int qeth_core_ethtool_get_link_ksettings(struct net_device *netdev,
-                                        struct ethtool_link_ksettings *cmd);
 int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback);
 int qeth_configure_cq(struct qeth_card *, enum qeth_cq);
 int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action);
@@ -1036,14 +1014,16 @@ netdev_features_t qeth_fix_features(struct net_device *, netdev_features_t);
 netdev_features_t qeth_features_check(struct sk_buff *skb,
                                      struct net_device *dev,
                                      netdev_features_t features);
+void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats);
 int qeth_open(struct net_device *dev);
 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, int cast_type,
-             void (*fill_header)(struct qeth_card *card, struct qeth_hdr *hdr,
-                                 struct sk_buff *skb, int ipv, int cast_type,
+             void (*fill_header)(struct qeth_qdio_out_q *queue,
+                                 struct qeth_hdr *hdr, struct sk_buff *skb,
+                                 int ipv, int cast_type,
                                  unsigned int data_len));
 
 /* exports for OSN */
index dcc06e4..4708df3 100644 (file)
@@ -74,8 +74,7 @@ static void qeth_notify_skbs(struct qeth_qdio_out_q *queue,
 static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf);
 static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int);
 
-struct workqueue_struct *qeth_wq;
-EXPORT_SYMBOL_GPL(qeth_wq);
+static struct workqueue_struct *qeth_wq;
 
 int qeth_card_hw_is_reachable(struct qeth_card *card)
 {
@@ -540,6 +539,7 @@ static int __qeth_issue_next_read(struct qeth_card *card)
                QETH_DBF_MESSAGE(2, "error %i on device %x when starting next read ccw!\n",
                                 rc, CARD_DEVID(card));
                atomic_set(&channel->irq_pending, 0);
+               qeth_release_buffer(channel, iob);
                card->read_or_write_problem = 1;
                qeth_schedule_recovery(card);
                wake_up(&card->wait_q);
@@ -566,6 +566,7 @@ static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card)
        if (reply) {
                refcount_set(&reply->refcnt, 1);
                atomic_set(&reply->received, 0);
+               init_waitqueue_head(&reply->wait_q);
        }
        return reply;
 }
@@ -581,6 +582,26 @@ static void qeth_put_reply(struct qeth_reply *reply)
                kfree(reply);
 }
 
+static void qeth_enqueue_reply(struct qeth_card *card, struct qeth_reply *reply)
+{
+       spin_lock_irq(&card->lock);
+       list_add_tail(&reply->list, &card->cmd_waiter_list);
+       spin_unlock_irq(&card->lock);
+}
+
+static void qeth_dequeue_reply(struct qeth_card *card, struct qeth_reply *reply)
+{
+       spin_lock_irq(&card->lock);
+       list_del(&reply->list);
+       spin_unlock_irq(&card->lock);
+}
+
+static void qeth_notify_reply(struct qeth_reply *reply)
+{
+       atomic_inc(&reply->received);
+       wake_up(&reply->wait_q);
+}
+
 static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc,
                struct qeth_card *card)
 {
@@ -657,19 +678,15 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
 
 void qeth_clear_ipacmd_list(struct qeth_card *card)
 {
-       struct qeth_reply *reply, *r;
+       struct qeth_reply *reply;
        unsigned long flags;
 
        QETH_CARD_TEXT(card, 4, "clipalst");
 
        spin_lock_irqsave(&card->lock, flags);
-       list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) {
-               qeth_get_reply(reply);
+       list_for_each_entry(reply, &card->cmd_waiter_list, list) {
                reply->rc = -EIO;
-               atomic_inc(&reply->received);
-               list_del_init(&reply->list);
-               wake_up(&reply->wait_q);
-               qeth_put_reply(reply);
+               qeth_notify_reply(reply);
        }
        spin_unlock_irqrestore(&card->lock, flags);
 }
@@ -726,7 +743,10 @@ void qeth_release_buffer(struct qeth_channel *channel,
        spin_lock_irqsave(&channel->iob_lock, flags);
        iob->state = BUF_STATE_FREE;
        iob->callback = qeth_send_control_data_cb;
-       iob->rc = 0;
+       if (iob->reply) {
+               qeth_put_reply(iob->reply);
+               iob->reply = NULL;
+       }
        spin_unlock_irqrestore(&channel->iob_lock, flags);
        wake_up(&channel->wait_q);
 }
@@ -739,6 +759,17 @@ static void qeth_release_buffer_cb(struct qeth_card *card,
        qeth_release_buffer(channel, iob);
 }
 
+static void qeth_cancel_cmd(struct qeth_cmd_buffer *iob, int rc)
+{
+       struct qeth_reply *reply = iob->reply;
+
+       if (reply) {
+               reply->rc = rc;
+               qeth_notify_reply(reply);
+       }
+       qeth_release_buffer(iob->channel, iob);
+}
+
 static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *channel)
 {
        struct qeth_cmd_buffer *buffer = NULL;
@@ -774,9 +805,9 @@ static void qeth_send_control_data_cb(struct qeth_card *card,
                                      struct qeth_cmd_buffer *iob)
 {
        struct qeth_ipa_cmd *cmd = NULL;
-       struct qeth_reply *reply, *r;
+       struct qeth_reply *reply = NULL;
+       struct qeth_reply *r;
        unsigned long flags;
-       int keep_reply;
        int rc = 0;
 
        QETH_CARD_TEXT(card, 4, "sndctlcb");
@@ -808,44 +839,40 @@ static void qeth_send_control_data_cb(struct qeth_card *card,
                        goto out;
        }
 
+       /* match against pending cmd requests */
        spin_lock_irqsave(&card->lock, flags);
-       list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) {
-               if ((reply->seqno == QETH_IDX_COMMAND_SEQNO) ||
-                   ((cmd) && (reply->seqno == cmd->hdr.seqno))) {
+       list_for_each_entry(r, &card->cmd_waiter_list, list) {
+               if ((r->seqno == QETH_IDX_COMMAND_SEQNO) ||
+                   (cmd && (r->seqno == cmd->hdr.seqno))) {
+                       reply = r;
+                       /* take the object outside the lock */
                        qeth_get_reply(reply);
-                       list_del_init(&reply->list);
-                       spin_unlock_irqrestore(&card->lock, flags);
-                       keep_reply = 0;
-                       if (reply->callback != NULL) {
-                               if (cmd) {
-                                       reply->offset = (__u16)((char *)cmd -
-                                                       (char *)iob->data);
-                                       keep_reply = reply->callback(card,
-                                                       reply,
-                                                       (unsigned long)cmd);
-                               } else
-                                       keep_reply = reply->callback(card,
-                                                       reply,
-                                                       (unsigned long)iob);
-                       }
-                       if (cmd)
-                               reply->rc = (u16) cmd->hdr.return_code;
-                       else if (iob->rc)
-                               reply->rc = iob->rc;
-                       if (keep_reply) {
-                               spin_lock_irqsave(&card->lock, flags);
-                               list_add_tail(&reply->list,
-                                             &card->cmd_waiter_list);
-                               spin_unlock_irqrestore(&card->lock, flags);
-                       } else {
-                               atomic_inc(&reply->received);
-                               wake_up(&reply->wait_q);
-                       }
-                       qeth_put_reply(reply);
-                       goto out;
+                       break;
                }
        }
        spin_unlock_irqrestore(&card->lock, flags);
+
+       if (!reply)
+               goto out;
+
+       if (!reply->callback) {
+               rc = 0;
+       } else {
+               if (cmd) {
+                       reply->offset = (u16)((char *)cmd - (char *)iob->data);
+                       rc = reply->callback(card, reply, (unsigned long)cmd);
+               } else {
+                       rc = reply->callback(card, reply, (unsigned long)iob);
+               }
+       }
+
+       if (rc <= 0) {
+               reply->rc = rc;
+               qeth_notify_reply(reply);
+       }
+
+       qeth_put_reply(reply);
+
 out:
        memcpy(&card->seqno.pdu_hdr_ack,
                QETH_PDU_HEADER_SEQ_NO(iob->data),
@@ -976,9 +1003,8 @@ static int qeth_get_problem(struct qeth_card *card, struct ccw_device *cdev,
        return 0;
 }
 
-static long qeth_check_irb_error(struct qeth_card *card,
-                                struct ccw_device *cdev, unsigned long intparm,
-                                struct irb *irb)
+static int qeth_check_irb_error(struct qeth_card *card, struct ccw_device *cdev,
+                               unsigned long intparm, struct irb *irb)
 {
        if (!IS_ERR(irb))
                return 0;
@@ -989,7 +1015,7 @@ static long qeth_check_irb_error(struct qeth_card *card,
                                 CCW_DEVID(cdev));
                QETH_CARD_TEXT(card, 2, "ckirberr");
                QETH_CARD_TEXT_(card, 2, "  rc%d", -EIO);
-               break;
+               return -EIO;
        case -ETIMEDOUT:
                dev_warn(&cdev->dev, "A hardware operation timed out"
                        " on the device\n");
@@ -1001,14 +1027,14 @@ static long qeth_check_irb_error(struct qeth_card *card,
                                wake_up(&card->wait_q);
                        }
                }
-               break;
+               return -ETIMEDOUT;
        default:
                QETH_DBF_MESSAGE(2, "unknown error %ld on channel %x\n",
                                 PTR_ERR(irb), CCW_DEVID(cdev));
                QETH_CARD_TEXT(card, 2, "ckirberr");
                QETH_CARD_TEXT(card, 2, "  rc???");
+               return PTR_ERR(irb);
        }
-       return PTR_ERR(irb);
 }
 
 static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
@@ -1043,10 +1069,11 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
        if (qeth_intparm_is_iob(intparm))
                iob = (struct qeth_cmd_buffer *) __va((addr_t)intparm);
 
-       if (qeth_check_irb_error(card, cdev, intparm, irb)) {
+       rc = qeth_check_irb_error(card, cdev, intparm, irb);
+       if (rc) {
                /* IO was terminated, free its resources. */
                if (iob)
-                       qeth_release_buffer(iob->channel, iob);
+                       qeth_cancel_cmd(iob, rc);
                atomic_set(&channel->irq_pending, 0);
                wake_up(&card->wait_q);
                return;
@@ -1101,6 +1128,8 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
                rc = qeth_get_problem(card, cdev, irb);
                if (rc) {
                        card->read_or_write_problem = 1;
+                       if (iob)
+                               qeth_cancel_cmd(iob, rc);
                        qeth_clear_ipacmd_list(card);
                        qeth_schedule_recovery(card);
                        goto out;
@@ -1262,7 +1291,6 @@ static int qeth_setup_channel(struct qeth_channel *channel, bool alloc_buffers)
                channel->iob[cnt].state = BUF_STATE_FREE;
                channel->iob[cnt].channel = channel;
                channel->iob[cnt].callback = qeth_send_control_data_cb;
-               channel->iob[cnt].rc = 0;
        }
        if (cnt < QETH_CMD_BUFFER_NO) {
                qeth_clean_channel(channel);
@@ -1439,6 +1467,10 @@ static struct qeth_card *qeth_alloc_card(struct ccwgroup_device *gdev)
        CARD_RDEV(card) = gdev->cdev[0];
        CARD_WDEV(card) = gdev->cdev[1];
        CARD_DDEV(card) = gdev->cdev[2];
+
+       card->event_wq = alloc_ordered_workqueue("%s", 0, dev_name(&gdev->dev));
+       if (!card->event_wq)
+               goto out_wq;
        if (qeth_setup_channel(&card->read, true))
                goto out_ip;
        if (qeth_setup_channel(&card->write, true))
@@ -1454,6 +1486,8 @@ out_data:
 out_channel:
        qeth_clean_channel(&card->read);
 out_ip:
+       destroy_workqueue(card->event_wq);
+out_wq:
        dev_set_drvdata(&gdev->dev, NULL);
        kfree(card);
 out:
@@ -1782,6 +1816,7 @@ static int qeth_idx_activate_get_answer(struct qeth_card *card,
                QETH_DBF_MESSAGE(2, "Error2 in activating channel rc=%d\n", rc);
                QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
                atomic_set(&channel->irq_pending, 0);
+               qeth_release_buffer(channel, iob);
                wake_up(&card->wait_q);
                return rc;
        }
@@ -1851,6 +1886,7 @@ static int qeth_idx_activate_channel(struct qeth_card *card,
                        rc);
                QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
                atomic_set(&channel->irq_pending, 0);
+               qeth_release_buffer(channel, iob);
                wake_up(&card->wait_q);
                return rc;
        }
@@ -1981,7 +2017,7 @@ void qeth_prepare_control_data(struct qeth_card *card, int len,
        card->seqno.pdu_hdr++;
        memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(iob->data),
               &card->seqno.pdu_hdr_ack, QETH_SEQ_NO_LENGTH);
-       QETH_DBF_HEX(CTRL, 2, iob->data, QETH_DBF_CTRL_LEN);
+       QETH_DBF_HEX(CTRL, 2, iob->data, min(len, QETH_DBF_CTRL_LEN));
 }
 EXPORT_SYMBOL_GPL(qeth_prepare_control_data);
 
@@ -1998,24 +2034,22 @@ EXPORT_SYMBOL_GPL(qeth_prepare_control_data);
  *                             for the IPA commands.
  * @reply_param:               private pointer passed to the callback
  *
- * Returns the value of the `return_code' field of the response
- * block returned from the hardware, or other error indication.
- * Value of zero indicates successful execution of the command.
- *
  * Callback function gets called one or more times, with cb_cmd
  * pointing to the response returned by the hardware. Callback
- * function must return non-zero if more reply blocks are expected,
- * and zero if the last or only reply block is received. Callback
- * function can get the value of the reply_param pointer from the
+ * function must return
+ *   > 0 if more reply blocks are expected,
+ *     0 if the last or only reply block is received, and
+ *   < 0 on error.
+ * Callback function can get the value of the reply_param pointer from the
  * field 'param' of the structure qeth_reply.
  */
 
-int qeth_send_control_data(struct qeth_card *card, int len,
-               struct qeth_cmd_buffer *iob,
-               int (*reply_cb)(struct qeth_card *cb_card,
-                               struct qeth_reply *cb_reply,
-                               unsigned long cb_cmd),
-               void *reply_param)
+static int qeth_send_control_data(struct qeth_card *card, int len,
+                                 struct qeth_cmd_buffer *iob,
+                                 int (*reply_cb)(struct qeth_card *cb_card,
+                                                 struct qeth_reply *cb_reply,
+                                                 unsigned long cb_cmd),
+                                 void *reply_param)
 {
        struct qeth_channel *channel = iob->channel;
        int rc;
@@ -2031,12 +2065,15 @@ int qeth_send_control_data(struct qeth_card *card, int len,
        }
        reply = qeth_alloc_reply(card);
        if (!reply) {
+               qeth_release_buffer(channel, iob);
                return -ENOMEM;
        }
        reply->callback = reply_cb;
        reply->param = reply_param;
 
-       init_waitqueue_head(&reply->wait_q);
+       /* pairs with qeth_release_buffer(): */
+       qeth_get_reply(reply);
+       iob->reply = reply;
 
        while (atomic_cmpxchg(&channel->irq_pending, 0, 1)) ;
 
@@ -2051,9 +2088,7 @@ int qeth_send_control_data(struct qeth_card *card, int len,
        }
        qeth_prepare_control_data(card, len, iob);
 
-       spin_lock_irq(&card->lock);
-       list_add_tail(&reply->list, &card->cmd_waiter_list);
-       spin_unlock_irq(&card->lock);
+       qeth_enqueue_reply(card, reply);
 
        timeout = jiffies + event_timeout;
 
@@ -2066,10 +2101,8 @@ int qeth_send_control_data(struct qeth_card *card, int len,
                QETH_DBF_MESSAGE(2, "qeth_send_control_data on device %x: ccw_device_start rc = %i\n",
                                 CARD_DEVID(card), rc);
                QETH_CARD_TEXT_(card, 2, " err%d", rc);
-               spin_lock_irq(&card->lock);
-               list_del_init(&reply->list);
+               qeth_dequeue_reply(card, reply);
                qeth_put_reply(reply);
-               spin_unlock_irq(&card->lock);
                qeth_release_buffer(channel, iob);
                atomic_set(&channel->irq_pending, 0);
                wake_up(&card->wait_q);
@@ -2091,21 +2124,16 @@ int qeth_send_control_data(struct qeth_card *card, int len,
                }
        }
 
+       qeth_dequeue_reply(card, reply);
        rc = reply->rc;
        qeth_put_reply(reply);
        return rc;
 
 time_err:
-       reply->rc = -ETIME;
-       spin_lock_irq(&card->lock);
-       list_del_init(&reply->list);
-       spin_unlock_irq(&card->lock);
-       atomic_inc(&reply->received);
-       rc = reply->rc;
+       qeth_dequeue_reply(card, reply);
        qeth_put_reply(reply);
-       return rc;
+       return -ETIME;
 }
-EXPORT_SYMBOL_GPL(qeth_send_control_data);
 
 static int qeth_cm_enable_cb(struct qeth_card *card, struct qeth_reply *reply,
                unsigned long data)
@@ -2310,9 +2338,8 @@ static int qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply,
                QETH_DBF_TEXT(SETUP, 2, "olmlimit");
                dev_err(&card->gdev->dev, "A connection could not be "
                        "established because of an OLM limit\n");
-               iob->rc = -EMLINK;
+               return -EMLINK;
        }
-       QETH_DBF_TEXT_(SETUP, 2, "  rc%d", iob->rc);
        return 0;
 }
 
@@ -2362,11 +2389,12 @@ static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *q, int bidx)
        return 0;
 }
 
-static void qeth_free_qdio_out_buf(struct qeth_qdio_out_q *q)
+static void qeth_free_output_queue(struct qeth_qdio_out_q *q)
 {
        if (!q)
                return;
 
+       qeth_clear_outq_buffers(q, 1);
        qdio_free_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q);
        kfree(q);
 }
@@ -2405,12 +2433,6 @@ static int qeth_alloc_qdio_buffers(struct qeth_card *card)
                goto out_freeinq;
 
        /* outbound */
-       card->qdio.out_qs =
-               kcalloc(card->qdio.no_out_queues,
-                       sizeof(struct qeth_qdio_out_q *),
-                       GFP_KERNEL);
-       if (!card->qdio.out_qs)
-               goto out_freepool;
        for (i = 0; i < card->qdio.no_out_queues; ++i) {
                card->qdio.out_qs[i] = qeth_alloc_qdio_out_buf();
                if (!card->qdio.out_qs[i])
@@ -2441,12 +2463,9 @@ out_freeoutqbufs:
        }
 out_freeoutq:
        while (i > 0) {
-               qeth_free_qdio_out_buf(card->qdio.out_qs[--i]);
-               qeth_clear_outq_buffers(card->qdio.out_qs[i], 1);
+               qeth_free_output_queue(card->qdio.out_qs[--i]);
+               card->qdio.out_qs[i] = NULL;
        }
-       kfree(card->qdio.out_qs);
-       card->qdio.out_qs = NULL;
-out_freepool:
        qeth_free_buffer_pool(card);
 out_freeinq:
        qeth_free_qdio_queue(card->qdio.in_q);
@@ -2475,13 +2494,9 @@ static void qeth_free_qdio_buffers(struct qeth_card *card)
        /* inbound buffer pool */
        qeth_free_buffer_pool(card);
        /* free outbound qdio_qs */
-       if (card->qdio.out_qs) {
-               for (i = 0; i < card->qdio.no_out_queues; ++i) {
-                       qeth_clear_outq_buffers(card->qdio.out_qs[i], 1);
-                       qeth_free_qdio_out_buf(card->qdio.out_qs[i]);
-               }
-               kfree(card->qdio.out_qs);
-               card->qdio.out_qs = NULL;
+       for (i = 0; i < card->qdio.no_out_queues; i++) {
+               qeth_free_output_queue(card->qdio.out_qs[i]);
+               card->qdio.out_qs[i] = NULL;
        }
 }
 
@@ -2688,8 +2703,7 @@ static struct qeth_buffer_pool_entry *qeth_find_free_buffer_pool_entry(
                        } else {
                                free_page((unsigned long)entry->elements[i]);
                                entry->elements[i] = page_address(page);
-                               if (card->options.performance_stats)
-                                       card->perf_stats.sg_alloc_page_rx++;
+                               QETH_CARD_STAT_INC(card, rx_sg_alloc_page);
                        }
                }
        }
@@ -2808,14 +2822,20 @@ static void qeth_fill_ipacmd_header(struct qeth_card *card,
        cmd->hdr.prot_version = prot;
 }
 
-void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob)
+void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+                         u16 cmd_length)
 {
+       u16 total_length = IPA_PDU_HEADER_SIZE + cmd_length;
        u8 prot_type = qeth_mpc_select_prot_type(card);
 
        memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
+       memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &total_length, 2);
        memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data), &prot_type, 1);
+       memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &cmd_length, 2);
+       memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &cmd_length, 2);
        memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
               &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
+       memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &cmd_length, 2);
 }
 EXPORT_SYMBOL_GPL(qeth_prepare_ipa_cmd);
 
@@ -2826,7 +2846,7 @@ struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *card,
 
        iob = qeth_get_buffer(&card->write);
        if (iob) {
-               qeth_prepare_ipa_cmd(card, iob);
+               qeth_prepare_ipa_cmd(card, iob, sizeof(struct qeth_ipa_cmd));
                qeth_fill_ipacmd_header(card, __ipa_cmd(iob), ipacmd, prot);
        } else {
                dev_warn(&card->gdev->dev,
@@ -2839,6 +2859,14 @@ struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *card,
 }
 EXPORT_SYMBOL_GPL(qeth_get_ipacmd_buffer);
 
+static int qeth_send_ipa_cmd_cb(struct qeth_card *card,
+                               struct qeth_reply *reply, unsigned long data)
+{
+       struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
+
+       return (cmd->hdr.return_code) ? -EIO : 0;
+}
+
 /**
  * qeth_send_ipa_cmd() - send an IPA command
  *
@@ -2850,11 +2878,15 @@ int qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
                        unsigned long),
                void *reply_param)
 {
+       u16 length;
        int rc;
 
        QETH_CARD_TEXT(card, 4, "sendipa");
-       rc = qeth_send_control_data(card, IPA_CMD_LENGTH,
-                                               iob, reply_cb, reply_param);
+
+       if (reply_cb == NULL)
+               reply_cb = qeth_send_ipa_cmd_cb;
+       memcpy(&length, QETH_IPA_PDU_LEN_TOTAL(iob->data), 2);
+       rc = qeth_send_control_data(card, length, iob, reply_cb, reply_param);
        if (rc == -ETIME) {
                qeth_clear_ipacmd_list(card);
                qeth_schedule_recovery(card);
@@ -2863,9 +2895,19 @@ int qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
 }
 EXPORT_SYMBOL_GPL(qeth_send_ipa_cmd);
 
+static int qeth_send_startlan_cb(struct qeth_card *card,
+                                struct qeth_reply *reply, unsigned long data)
+{
+       struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
+
+       if (cmd->hdr.return_code == IPA_RC_LAN_OFFLINE)
+               return -ENETDOWN;
+
+       return (cmd->hdr.return_code) ? -EIO : 0;
+}
+
 static int qeth_send_startlan(struct qeth_card *card)
 {
-       int rc;
        struct qeth_cmd_buffer *iob;
 
        QETH_DBF_TEXT(SETUP, 2, "strtlan");
@@ -2873,8 +2915,7 @@ static int qeth_send_startlan(struct qeth_card *card)
        iob = qeth_get_ipacmd_buffer(card, IPA_CMD_STARTLAN, 0);
        if (!iob)
                return -ENOMEM;
-       rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
-       return rc;
+       return qeth_send_ipa_cmd(card, iob, qeth_send_startlan_cb, NULL);
 }
 
 static int qeth_setadpparms_inspect_rc(struct qeth_ipa_cmd *cmd)
@@ -2892,7 +2933,7 @@ static int qeth_query_setadapterparms_cb(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 3, "quyadpcb");
        if (qeth_setadpparms_inspect_rc(cmd))
-               return 0;
+               return -EIO;
 
        if (cmd->data.setadapterparms.data.query_cmds_supp.lan_type & 0x7f) {
                card->info.link_type =
@@ -2947,19 +2988,18 @@ static int qeth_query_ipassists_cb(struct qeth_card *card,
        cmd = (struct qeth_ipa_cmd *) data;
 
        switch (cmd->hdr.return_code) {
+       case IPA_RC_SUCCESS:
+               break;
        case IPA_RC_NOTSUPP:
        case IPA_RC_L2_UNSUPPORTED_CMD:
                QETH_DBF_TEXT(SETUP, 2, "ipaunsup");
                card->options.ipa4.supported_funcs |= IPA_SETADAPTERPARMS;
                card->options.ipa6.supported_funcs |= IPA_SETADAPTERPARMS;
-               return 0;
+               return -EOPNOTSUPP;
        default:
-               if (cmd->hdr.return_code) {
-                       QETH_DBF_MESSAGE(1, "IPA_CMD_QIPASSIST on device %x: Unhandled rc=%#x\n",
-                                        CARD_DEVID(card),
-                                        cmd->hdr.return_code);
-                       return 0;
-               }
+               QETH_DBF_MESSAGE(1, "IPA_CMD_QIPASSIST on device %x: Unhandled rc=%#x\n",
+                                CARD_DEVID(card), cmd->hdr.return_code);
+               return -EIO;
        }
 
        if (cmd->hdr.prot_version == QETH_PROT_IPV4) {
@@ -2997,7 +3037,7 @@ static int qeth_query_switch_attributes_cb(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 2, "qswiatcb");
        if (qeth_setadpparms_inspect_rc(cmd))
-               return 0;
+               return -EIO;
 
        sw_info = (struct qeth_switch_info *)reply->param;
        attrs = &cmd->data.setadapterparms.data.query_switch_attributes;
@@ -3029,15 +3069,15 @@ int qeth_query_switch_attributes(struct qeth_card *card,
 static int qeth_query_setdiagass_cb(struct qeth_card *card,
                struct qeth_reply *reply, unsigned long data)
 {
-       struct qeth_ipa_cmd *cmd;
-       __u16 rc;
+       struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
+       u16 rc = cmd->hdr.return_code;
 
-       cmd = (struct qeth_ipa_cmd *)data;
-       rc = cmd->hdr.return_code;
-       if (rc)
+       if (rc) {
                QETH_CARD_TEXT_(card, 2, "diagq:%x", rc);
-       else
-               card->info.diagass_support = cmd->data.diagass.ext;
+               return -EIO;
+       }
+
+       card->info.diagass_support = cmd->data.diagass.ext;
        return 0;
 }
 
@@ -3084,13 +3124,13 @@ static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid)
 static int qeth_hw_trap_cb(struct qeth_card *card,
                struct qeth_reply *reply, unsigned long data)
 {
-       struct qeth_ipa_cmd *cmd;
-       __u16 rc;
+       struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
+       u16 rc = cmd->hdr.return_code;
 
-       cmd = (struct qeth_ipa_cmd *)data;
-       rc = cmd->hdr.return_code;
-       if (rc)
+       if (rc) {
                QETH_CARD_TEXT_(card, 2, "trapc:%x", rc);
+               return -EIO;
+       }
        return 0;
 }
 
@@ -3139,7 +3179,7 @@ static int qeth_check_qdio_errors(struct qeth_card *card,
                               buf->element[14].sflags);
                QETH_CARD_TEXT_(card, 2, " qerr=%X", qdio_error);
                if ((buf->element[15].sflags) == 0x12) {
-                       card->stats.rx_dropped++;
+                       QETH_CARD_STAT_INC(card, rx_dropped);
                        return 0;
                } else
                        return 1;
@@ -3203,17 +3243,8 @@ static void qeth_queue_input_buffer(struct qeth_card *card, int index)
                 * 'index') un-requeued -> this buffer is the first buffer that
                 * will be requeued the next time
                 */
-               if (card->options.performance_stats) {
-                       card->perf_stats.inbound_do_qdio_cnt++;
-                       card->perf_stats.inbound_do_qdio_start_time =
-                               qeth_get_micros();
-               }
                rc = do_QDIO(CARD_DDEV(card), QDIO_FLAG_SYNC_INPUT, 0,
                             queue->next_buf_to_init, count);
-               if (card->options.performance_stats)
-                       card->perf_stats.inbound_do_qdio_time +=
-                               qeth_get_micros() -
-                               card->perf_stats.inbound_do_qdio_start_time;
                if (rc) {
                        QETH_CARD_TEXT(card, 2, "qinberr");
                }
@@ -3290,8 +3321,7 @@ static void qeth_switch_to_packing_if_needed(struct qeth_qdio_out_q *queue)
                    >= QETH_HIGH_WATERMARK_PACK){
                        /* switch non-PACKING -> PACKING */
                        QETH_CARD_TEXT(queue->card, 6, "np->pack");
-                       if (queue->card->options.performance_stats)
-                               queue->card->perf_stats.sc_dp_p++;
+                       QETH_TXQ_STAT_INC(queue, packing_mode_switch);
                        queue->do_pack = 1;
                }
        }
@@ -3310,8 +3340,7 @@ static int qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue)
                    <= QETH_LOW_WATERMARK_PACK) {
                        /* switch PACKING -> non-PACKING */
                        QETH_CARD_TEXT(queue->card, 6, "pack->np");
-                       if (queue->card->options.performance_stats)
-                               queue->card->perf_stats.sc_p_dp++;
+                       QETH_TXQ_STAT_INC(queue, packing_mode_switch);
                        queue->do_pack = 0;
                        return qeth_prep_flush_pack_buffer(queue);
                }
@@ -3365,25 +3394,16 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
                }
        }
 
+       QETH_TXQ_STAT_ADD(queue, bufs, count);
        netif_trans_update(queue->card->dev);
-       if (queue->card->options.performance_stats) {
-               queue->card->perf_stats.outbound_do_qdio_cnt++;
-               queue->card->perf_stats.outbound_do_qdio_start_time =
-                       qeth_get_micros();
-       }
        qdio_flags = QDIO_FLAG_SYNC_OUTPUT;
        if (atomic_read(&queue->set_pci_flags_count))
                qdio_flags |= QDIO_FLAG_PCI_OUT;
        atomic_add(count, &queue->used_buffers);
-
        rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags,
                     queue->queue_no, index, count);
-       if (queue->card->options.performance_stats)
-               queue->card->perf_stats.outbound_do_qdio_time +=
-                       qeth_get_micros() -
-                       queue->card->perf_stats.outbound_do_qdio_start_time;
        if (rc) {
-               queue->card->stats.tx_errors += count;
+               QETH_TXQ_STAT_ADD(queue, tx_errors, count);
                /* ignore temporary SIGA errors without busy condition */
                if (rc == -ENOBUFS)
                        return;
@@ -3398,8 +3418,6 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
                qeth_schedule_recovery(queue->card);
                return;
        }
-       if (queue->card->options.performance_stats)
-               queue->card->perf_stats.bufs_sent += count;
 }
 
 static void qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
@@ -3430,10 +3448,8 @@ static void qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
                        if (!flush_cnt &&
                            !atomic_read(&queue->set_pci_flags_count))
                                flush_cnt += qeth_prep_flush_pack_buffer(queue);
-                       if (queue->card->options.performance_stats &&
-                           q_was_packing)
-                               queue->card->perf_stats.bufs_sent_pack +=
-                                       flush_cnt;
+                       if (q_was_packing)
+                               QETH_TXQ_STAT_ADD(queue, bufs_pack, flush_cnt);
                        if (flush_cnt)
                                qeth_flush_buffers(queue, index, flush_cnt);
                        atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
@@ -3488,7 +3504,7 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err,
        int rc;
 
        if (!qeth_is_cq(card, queue))
-               goto out;
+               return;
 
        QETH_CARD_TEXT_(card, 5, "qcqhe%d", first_element);
        QETH_CARD_TEXT_(card, 5, "qcqhc%d", count);
@@ -3497,12 +3513,7 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err,
        if (qdio_err) {
                netif_stop_queue(card->dev);
                qeth_schedule_recovery(card);
-               goto out;
-       }
-
-       if (card->options.performance_stats) {
-               card->perf_stats.cq_cnt++;
-               card->perf_stats.cq_start_time = qeth_get_micros();
+               return;
        }
 
        for (i = first_element; i < first_element + count; ++i) {
@@ -3530,14 +3541,6 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err,
        }
        card->qdio.c_q->next_buf_to_init = (card->qdio.c_q->next_buf_to_init
                                   + count) % QDIO_MAX_BUFFERS_PER_Q;
-
-       if (card->options.performance_stats) {
-               int delta_t = qeth_get_micros();
-               delta_t -= card->perf_stats.cq_start_time;
-               card->perf_stats.cq_time += delta_t;
-       }
-out:
-       return;
 }
 
 static void qeth_qdio_input_handler(struct ccw_device *ccwdev,
@@ -3573,11 +3576,7 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev,
                qeth_schedule_recovery(card);
                return;
        }
-       if (card->options.performance_stats) {
-               card->perf_stats.outbound_handler_cnt++;
-               card->perf_stats.outbound_handler_start_time =
-                       qeth_get_micros();
-       }
+
        for (i = first_element; i < (first_element + count); ++i) {
                int bidx = i % QDIO_MAX_BUFFERS_PER_Q;
                buffer = queue->bufs[bidx];
@@ -3623,9 +3622,6 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev,
                qeth_check_outbound_queue(queue);
 
        netif_wake_queue(queue->card->dev);
-       if (card->options.performance_stats)
-               card->perf_stats.outbound_handler_time += qeth_get_micros() -
-                       card->perf_stats.outbound_handler_start_time;
 }
 
 /* We cannot use outbound queue 3 for unicast packets on HiperSockets */
@@ -3746,11 +3742,12 @@ EXPORT_SYMBOL_GPL(qeth_count_elements);
  * The number of needed buffer elements is returned in @elements.
  * Error to create the hdr is indicated by returning with < 0.
  */
-static int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb,
-                             struct qeth_hdr **hdr, unsigned int hdr_len,
-                             unsigned int proto_len, unsigned int *elements)
+static int qeth_add_hw_header(struct qeth_qdio_out_q *queue,
+                             struct sk_buff *skb, struct qeth_hdr **hdr,
+                             unsigned int hdr_len, unsigned int proto_len,
+                             unsigned int *elements)
 {
-       const unsigned int max_elements = QETH_MAX_BUFFER_ELEMENTS(card);
+       const unsigned int max_elements = QETH_MAX_BUFFER_ELEMENTS(queue->card);
        const unsigned int contiguous = proto_len ? proto_len : 1;
        unsigned int __elements;
        addr_t start, end;
@@ -3789,15 +3786,12 @@ check_layout:
                }
 
                rc = skb_linearize(skb);
-               if (card->options.performance_stats) {
-                       if (rc)
-                               card->perf_stats.tx_linfail++;
-                       else
-                               card->perf_stats.tx_lin++;
-               }
-               if (rc)
+               if (rc) {
+                       QETH_TXQ_STAT_INC(queue, skbs_linearized_fail);
                        return rc;
+               }
 
+               QETH_TXQ_STAT_INC(queue, skbs_linearized);
                /* Linearization changed the layout, re-evaluate: */
                goto check_layout;
        }
@@ -3921,9 +3915,8 @@ static int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
                QETH_CARD_TEXT(queue->card, 6, "fillbfnp");
        } else {
                QETH_CARD_TEXT(queue->card, 6, "fillbfpa");
-               if (queue->card->options.performance_stats)
-                       queue->card->perf_stats.skbs_sent_pack++;
 
+               QETH_TXQ_STAT_INC(queue, skbs_pack);
                /* If the buffer still has free elements, keep using it. */
                if (buf->next_element_to_fill <
                    QETH_MAX_BUFFER_ELEMENTS(queue->card))
@@ -4037,8 +4030,8 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
        }
 out:
        /* at this point the queue is UNLOCKED again */
-       if (queue->card->options.performance_stats && do_pack)
-               queue->card->perf_stats.bufs_sent_pack += flush_count;
+       if (do_pack)
+               QETH_TXQ_STAT_ADD(queue, bufs_pack, flush_count);
 
        return rc;
 }
@@ -4062,8 +4055,9 @@ 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, int cast_type,
-             void (*fill_header)(struct qeth_card *card, struct qeth_hdr *hdr,
-                                 struct sk_buff *skb, int ipv, int cast_type,
+             void (*fill_header)(struct qeth_qdio_out_q *queue,
+                                 struct qeth_hdr *hdr, struct sk_buff *skb,
+                                 int ipv, int cast_type,
                                  unsigned int data_len))
 {
        unsigned int proto_len, hw_hdr_len;
@@ -4088,7 +4082,7 @@ int qeth_xmit(struct qeth_card *card, struct sk_buff *skb,
        if (rc)
                return rc;
 
-       push_len = qeth_add_hw_header(card, skb, &hdr, hw_hdr_len, proto_len,
+       push_len = qeth_add_hw_header(queue, skb, &hdr, hw_hdr_len, proto_len,
                                      &elements);
        if (push_len < 0)
                return push_len;
@@ -4098,7 +4092,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(card, hdr, skb, ipv, cast_type, frame_len);
+       fill_header(queue, hdr, skb, ipv, cast_type, frame_len);
        if (is_tso)
                qeth_fill_tso_ext((struct qeth_hdr_tso *) hdr,
                                  frame_len - proto_len, skb, proto_len);
@@ -4115,14 +4109,12 @@ int qeth_xmit(struct qeth_card *card, struct sk_buff *skb,
        }
 
        if (!rc) {
-               if (card->options.performance_stats) {
-                       card->perf_stats.buf_elements_sent += elements;
-                       if (is_sg)
-                               card->perf_stats.sg_skbs_sent++;
-                       if (is_tso) {
-                               card->perf_stats.large_send_bytes += frame_len;
-                               card->perf_stats.large_send_cnt++;
-                       }
+               QETH_TXQ_STAT_ADD(queue, buf_elements, elements);
+               if (is_sg)
+                       QETH_TXQ_STAT_INC(queue, skbs_sg);
+               if (is_tso) {
+                       QETH_TXQ_STAT_INC(queue, skbs_tso);
+                       QETH_TXQ_STAT_ADD(queue, tso_bytes, frame_len);
                }
        } else {
                if (!push_len)
@@ -4149,7 +4141,7 @@ static int qeth_setadp_promisc_mode_cb(struct qeth_card *card,
                setparms->data.mode = SET_PROMISC_MODE_OFF;
        }
        card->info.promisc_mode = setparms->data.mode;
-       return 0;
+       return (cmd->hdr.return_code) ? -EIO : 0;
 }
 
 void qeth_setadp_promisc_mode(struct qeth_card *card)
@@ -4181,18 +4173,6 @@ void qeth_setadp_promisc_mode(struct qeth_card *card)
 }
 EXPORT_SYMBOL_GPL(qeth_setadp_promisc_mode);
 
-struct net_device_stats *qeth_get_stats(struct net_device *dev)
-{
-       struct qeth_card *card;
-
-       card = dev->ml_priv;
-
-       QETH_CARD_TEXT(card, 5, "getstat");
-
-       return &card->stats;
-}
-EXPORT_SYMBOL_GPL(qeth_get_stats);
-
 static int qeth_setadpparms_change_macaddr_cb(struct qeth_card *card,
                struct qeth_reply *reply, unsigned long data)
 {
@@ -4201,12 +4181,15 @@ static int qeth_setadpparms_change_macaddr_cb(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 4, "chgmaccb");
        if (qeth_setadpparms_inspect_rc(cmd))
-               return 0;
+               return -EIO;
 
        adp_cmd = &cmd->data.setadapterparms;
+       if (!is_valid_ether_addr(adp_cmd->data.change_addr.addr))
+               return -EADDRNOTAVAIL;
+
        if (IS_LAYER2(card) && IS_OSD(card) && !IS_VM_NIC(card) &&
            !(adp_cmd->hdr.flags & QETH_SETADP_FLAGS_VIRTUAL_MAC))
-               return 0;
+               return -EADDRNOTAVAIL;
 
        ether_addr_copy(card->dev->dev_addr, adp_cmd->data.change_addr.addr);
        return 0;
@@ -4245,7 +4228,7 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 4, "setaccb");
        if (cmd->hdr.return_code)
-               return 0;
+               return -EIO;
        qeth_setadpparms_inspect_rc(cmd);
 
        access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl;
@@ -4319,7 +4302,7 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,
                        card->options.isolation = card->options.prev_isolation;
                break;
        }
-       return 0;
+       return (cmd->hdr.return_code) ? -EIO : 0;
 }
 
 static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card,
@@ -4383,7 +4366,7 @@ void qeth_tx_timeout(struct net_device *dev)
 
        card = dev->ml_priv;
        QETH_CARD_TEXT(card, 4, "txtimeo");
-       card->stats.tx_errors++;
+       QETH_CARD_STAT_INC(card, tx_errors);
        qeth_schedule_recovery(card);
 }
 EXPORT_SYMBOL_GPL(qeth_tx_timeout);
@@ -4453,27 +4436,6 @@ static int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum)
        return rc;
 }
 
-static int qeth_send_ipa_snmp_cmd(struct qeth_card *card,
-               struct qeth_cmd_buffer *iob, int len,
-               int (*reply_cb)(struct qeth_card *, struct qeth_reply *,
-                       unsigned long),
-               void *reply_param)
-{
-       u16 s1, s2;
-
-       QETH_CARD_TEXT(card, 4, "sendsnmp");
-
-       /* adjust PDU length fields in IPA_PDU_HEADER */
-       s1 = (u32) IPA_PDU_HEADER_SIZE + len;
-       s2 = (u32) len;
-       memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2);
-       memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2);
-       memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2);
-       memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2);
-       return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob,
-                                     reply_cb, reply_param);
-}
-
 static int qeth_snmp_command_cb(struct qeth_card *card,
                struct qeth_reply *reply, unsigned long sdata)
 {
@@ -4491,13 +4453,13 @@ static int qeth_snmp_command_cb(struct qeth_card *card,
 
        if (cmd->hdr.return_code) {
                QETH_CARD_TEXT_(card, 4, "scer1%x", cmd->hdr.return_code);
-               return 0;
+               return -EIO;
        }
        if (cmd->data.setadapterparms.hdr.return_code) {
                cmd->hdr.return_code =
                        cmd->data.setadapterparms.hdr.return_code;
                QETH_CARD_TEXT_(card, 4, "scer2%x", cmd->hdr.return_code);
-               return 0;
+               return -EIO;
        }
        data_len = *((__u16 *)QETH_IPA_PDU_LEN_PDU1(data));
        if (cmd->data.setadapterparms.hdr.seq_no == 1) {
@@ -4512,9 +4474,8 @@ static int qeth_snmp_command_cb(struct qeth_card *card,
 
        /* check if there is enough room in userspace */
        if ((qinfo->udata_len - qinfo->udata_offset) < data_len) {
-               QETH_CARD_TEXT_(card, 4, "scer3%i", -ENOMEM);
-               cmd->hdr.return_code = IPA_RC_ENOMEM;
-               return 0;
+               QETH_CARD_TEXT_(card, 4, "scer3%i", -ENOSPC);
+               return -ENOSPC;
        }
        QETH_CARD_TEXT_(card, 4, "snore%i",
                       cmd->data.setadapterparms.hdr.used_total);
@@ -4579,10 +4540,13 @@ static int qeth_snmp_command(struct qeth_card *card, char __user *udata)
                rc = -ENOMEM;
                goto out;
        }
+
+       /* for large requests, fix-up the length fields: */
+       qeth_prepare_ipa_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len);
+
        cmd = __ipa_cmd(iob);
        memcpy(&cmd->data.setadapterparms.data.snmp, &ureq->cmd, req_len);
-       rc = qeth_send_ipa_snmp_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len,
-                                   qeth_snmp_command_cb, (void *)&qinfo);
+       rc = qeth_send_ipa_cmd(card, iob, qeth_snmp_command_cb, &qinfo);
        if (rc)
                QETH_DBF_MESSAGE(2, "SNMP command failed on device %x: (%#x)\n",
                                 CARD_DEVID(card), rc);
@@ -4606,16 +4570,14 @@ static int qeth_setadpparms_query_oat_cb(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 3, "qoatcb");
        if (qeth_setadpparms_inspect_rc(cmd))
-               return 0;
+               return -EIO;
 
        priv = (struct qeth_qoat_priv *)reply->param;
        resdatalen = cmd->data.setadapterparms.hdr.cmdlength;
        resdata = (char *)data + 28;
 
-       if (resdatalen > (priv->buffer_len - priv->response_len)) {
-               cmd->hdr.return_code = IPA_RC_FFFF;
-               return 0;
-       }
+       if (resdatalen > (priv->buffer_len - priv->response_len))
+               return -ENOSPC;
 
        memcpy((priv->buffer + priv->response_len), resdata,
                resdatalen);
@@ -4688,9 +4650,7 @@ static int qeth_query_oat_command(struct qeth_card *card, char __user *udata)
                if (copy_to_user(udata, &oat_data,
                    sizeof(struct qeth_query_oat_data)))
                        rc = -EFAULT;
-       } else
-               if (rc == IPA_RC_FFFF)
-                       rc = -EFAULT;
+       }
 
 out_free:
        vfree(priv.buffer);
@@ -4707,7 +4667,7 @@ static int qeth_query_card_info_cb(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 2, "qcrdincb");
        if (qeth_setadpparms_inspect_rc(cmd))
-               return 0;
+               return -EIO;
 
        card_info = &cmd->data.setadapterparms.data.card_info;
        carrier_info->card_type = card_info->card_type;
@@ -4716,8 +4676,8 @@ static int qeth_query_card_info_cb(struct qeth_card *card,
        return 0;
 }
 
-static int qeth_query_card_info(struct qeth_card *card,
-                               struct carrier_info *carrier_info)
+int qeth_query_card_info(struct qeth_card *card,
+                        struct carrier_info *carrier_info)
 {
        struct qeth_cmd_buffer *iob;
 
@@ -4994,6 +4954,7 @@ static void qeth_core_free_card(struct qeth_card *card)
        qeth_clean_channel(&card->read);
        qeth_clean_channel(&card->write);
        qeth_clean_channel(&card->data);
+       destroy_workqueue(card->event_wq);
        qeth_free_qdio_buffers(card);
        unregister_service_level(&card->qeth_service_level);
        dev_set_drvdata(&card->gdev->dev, NULL);
@@ -5108,12 +5069,10 @@ retriable:
        rc = qeth_send_startlan(card);
        if (rc) {
                QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
-               if (rc == IPA_RC_LAN_OFFLINE) {
-                       dev_warn(&card->gdev->dev,
-                               "The LAN is offline\n");
+               if (rc == -ENETDOWN) {
+                       dev_warn(&card->gdev->dev, "The LAN is offline\n");
                        *carrier_ok = false;
                } else {
-                       rc = -ENODEV;
                        goto out;
                }
        } else {
@@ -5265,7 +5224,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
                                QETH_CARD_TEXT(card, 4, "unexeob");
                                QETH_CARD_HEX(card, 2, buffer, sizeof(void *));
                                dev_kfree_skb_any(skb);
-                               card->stats.rx_errors++;
+                               QETH_CARD_STAT_INC(card, rx_errors);
                                return NULL;
                        }
                        element++;
@@ -5277,16 +5236,17 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
        }
        *__element = element;
        *__offset = offset;
-       if (use_rx_sg && card->options.performance_stats) {
-               card->perf_stats.sg_skbs_rx++;
-               card->perf_stats.sg_frags_rx += skb_shinfo(skb)->nr_frags;
+       if (use_rx_sg) {
+               QETH_CARD_STAT_INC(card, rx_sg_skbs);
+               QETH_CARD_STAT_ADD(card, rx_sg_frags,
+                                  skb_shinfo(skb)->nr_frags);
        }
        return skb;
 no_mem:
        if (net_ratelimit()) {
                QETH_CARD_TEXT(card, 2, "noskbmem");
        }
-       card->stats.rx_dropped++;
+       QETH_CARD_STAT_INC(card, rx_dropped);
        return NULL;
 }
 EXPORT_SYMBOL_GPL(qeth_core_get_next_skb);
@@ -5299,11 +5259,6 @@ int qeth_poll(struct napi_struct *napi, int budget)
        int done;
        int new_budget = budget;
 
-       if (card->options.performance_stats) {
-               card->perf_stats.inbound_cnt++;
-               card->perf_stats.inbound_start_time = qeth_get_micros();
-       }
-
        while (1) {
                if (!card->rx.b_count) {
                        card->rx.qdio_err = 0;
@@ -5332,8 +5287,7 @@ int qeth_poll(struct napi_struct *napi, int budget)
                                done = 1;
 
                        if (done) {
-                               if (card->options.performance_stats)
-                                       card->perf_stats.bufs_rec++;
+                               QETH_CARD_STAT_INC(card, rx_bufs);
                                qeth_put_buffer_pool_entry(card,
                                        buffer->pool_entry);
                                qeth_queue_input_buffer(card, card->rx.b_index);
@@ -5361,9 +5315,6 @@ int qeth_poll(struct napi_struct *napi, int budget)
        if (qdio_start_irq(card->data.ccwdev, 0))
                napi_schedule(&card->napi);
 out:
-       if (card->options.performance_stats)
-               card->perf_stats.inbound_time += qeth_get_micros() -
-                       card->perf_stats.inbound_start_time;
        return work_done;
 }
 EXPORT_SYMBOL_GPL(qeth_poll);
@@ -5383,7 +5334,7 @@ static int qeth_setassparms_get_caps_cb(struct qeth_card *card,
        struct qeth_ipa_caps *caps = reply->param;
 
        if (qeth_setassparms_inspect_rc(cmd))
-               return 0;
+               return -EIO;
 
        caps->supported = cmd->data.setassparms.data.caps.supported;
        caps->enabled = cmd->data.setassparms.data.caps.enabled;
@@ -5393,18 +5344,18 @@ static int qeth_setassparms_get_caps_cb(struct qeth_card *card,
 int qeth_setassparms_cb(struct qeth_card *card,
                        struct qeth_reply *reply, unsigned long data)
 {
-       struct qeth_ipa_cmd *cmd;
+       struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
 
        QETH_CARD_TEXT(card, 4, "defadpcb");
 
-       cmd = (struct qeth_ipa_cmd *) data;
-       if (cmd->hdr.return_code == 0) {
-               cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
-               if (cmd->hdr.prot_version == QETH_PROT_IPV4)
-                       card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled;
-               if (cmd->hdr.prot_version == QETH_PROT_IPV6)
-                       card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
-       }
+       if (cmd->hdr.return_code)
+               return -EIO;
+
+       cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
+       if (cmd->hdr.prot_version == QETH_PROT_IPV4)
+               card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled;
+       if (cmd->hdr.prot_version == QETH_PROT_IPV6)
+               card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
        return 0;
 }
 EXPORT_SYMBOL_GPL(qeth_setassparms_cb);
@@ -5650,7 +5601,10 @@ static struct net_device *qeth_alloc_netdev(struct qeth_card *card)
        SET_NETDEV_DEV(dev, &card->gdev->dev);
        netif_carrier_off(dev);
 
-       if (!IS_OSN(card)) {
+       if (IS_OSN(card)) {
+               dev->ethtool_ops = &qeth_osn_ethtool_ops;
+       } else {
+               dev->ethtool_ops = &qeth_ethtool_ops;
                dev->priv_flags &= ~IFF_TX_SKB_SHARING;
                dev->hw_features |= NETIF_F_SG;
                dev->vlan_features |= NETIF_F_SG;
@@ -5896,9 +5850,6 @@ int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
        if (!card)
                return -ENODEV;
 
-       if (card->info.type == QETH_CARD_TYPE_OSN)
-               return -EPERM;
-
        switch (cmd) {
        case SIOC_QETH_ADP_SET_SNMP_CONTROL:
                rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data);
@@ -5938,452 +5889,91 @@ int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 }
 EXPORT_SYMBOL_GPL(qeth_do_ioctl);
 
-static struct {
-       const char str[ETH_GSTRING_LEN];
-} qeth_ethtool_stats_keys[] = {
-/*  0 */{"rx skbs"},
-       {"rx buffers"},
-       {"tx skbs"},
-       {"tx buffers"},
-       {"tx skbs no packing"},
-       {"tx buffers no packing"},
-       {"tx skbs packing"},
-       {"tx buffers packing"},
-       {"tx sg skbs"},
-       {"tx buffer elements"},
-/* 10 */{"rx sg skbs"},
-       {"rx sg frags"},
-       {"rx sg page allocs"},
-       {"tx large kbytes"},
-       {"tx large count"},
-       {"tx pk state ch n->p"},
-       {"tx pk state ch p->n"},
-       {"tx pk watermark low"},
-       {"tx pk watermark high"},
-       {"queue 0 buffer usage"},
-/* 20 */{"queue 1 buffer usage"},
-       {"queue 2 buffer usage"},
-       {"queue 3 buffer usage"},
-       {"rx poll time"},
-       {"rx poll count"},
-       {"rx do_QDIO time"},
-       {"rx do_QDIO count"},
-       {"tx handler time"},
-       {"tx handler count"},
-       {"tx time"},
-/* 30 */{"tx count"},
-       {"tx do_QDIO time"},
-       {"tx do_QDIO count"},
-       {"tx csum"},
-       {"tx lin"},
-       {"tx linfail"},
-       {"cq handler count"},
-       {"cq handler time"},
-       {"rx csum"}
-};
-
-int qeth_core_get_sset_count(struct net_device *dev, int stringset)
-{
-       switch (stringset) {
-       case ETH_SS_STATS:
-               return (sizeof(qeth_ethtool_stats_keys) / ETH_GSTRING_LEN);
-       default:
-               return -EINVAL;
-       }
-}
-EXPORT_SYMBOL_GPL(qeth_core_get_sset_count);
-
-void qeth_core_get_ethtool_stats(struct net_device *dev,
-               struct ethtool_stats *stats, u64 *data)
-{
-       struct qeth_card *card = dev->ml_priv;
-       data[0] = card->stats.rx_packets -
-                               card->perf_stats.initial_rx_packets;
-       data[1] = card->perf_stats.bufs_rec;
-       data[2] = card->stats.tx_packets -
-                               card->perf_stats.initial_tx_packets;
-       data[3] = card->perf_stats.bufs_sent;
-       data[4] = card->stats.tx_packets - card->perf_stats.initial_tx_packets
-                       - card->perf_stats.skbs_sent_pack;
-       data[5] = card->perf_stats.bufs_sent - card->perf_stats.bufs_sent_pack;
-       data[6] = card->perf_stats.skbs_sent_pack;
-       data[7] = card->perf_stats.bufs_sent_pack;
-       data[8] = card->perf_stats.sg_skbs_sent;
-       data[9] = card->perf_stats.buf_elements_sent;
-       data[10] = card->perf_stats.sg_skbs_rx;
-       data[11] = card->perf_stats.sg_frags_rx;
-       data[12] = card->perf_stats.sg_alloc_page_rx;
-       data[13] = (card->perf_stats.large_send_bytes >> 10);
-       data[14] = card->perf_stats.large_send_cnt;
-       data[15] = card->perf_stats.sc_dp_p;
-       data[16] = card->perf_stats.sc_p_dp;
-       data[17] = QETH_LOW_WATERMARK_PACK;
-       data[18] = QETH_HIGH_WATERMARK_PACK;
-       data[19] = atomic_read(&card->qdio.out_qs[0]->used_buffers);
-       data[20] = (card->qdio.no_out_queues > 1) ?
-                       atomic_read(&card->qdio.out_qs[1]->used_buffers) : 0;
-       data[21] = (card->qdio.no_out_queues > 2) ?
-                       atomic_read(&card->qdio.out_qs[2]->used_buffers) : 0;
-       data[22] = (card->qdio.no_out_queues > 3) ?
-                       atomic_read(&card->qdio.out_qs[3]->used_buffers) : 0;
-       data[23] = card->perf_stats.inbound_time;
-       data[24] = card->perf_stats.inbound_cnt;
-       data[25] = card->perf_stats.inbound_do_qdio_time;
-       data[26] = card->perf_stats.inbound_do_qdio_cnt;
-       data[27] = card->perf_stats.outbound_handler_time;
-       data[28] = card->perf_stats.outbound_handler_cnt;
-       data[29] = card->perf_stats.outbound_time;
-       data[30] = card->perf_stats.outbound_cnt;
-       data[31] = card->perf_stats.outbound_do_qdio_time;
-       data[32] = card->perf_stats.outbound_do_qdio_cnt;
-       data[33] = card->perf_stats.tx_csum;
-       data[34] = card->perf_stats.tx_lin;
-       data[35] = card->perf_stats.tx_linfail;
-       data[36] = card->perf_stats.cq_cnt;
-       data[37] = card->perf_stats.cq_time;
-       data[38] = card->perf_stats.rx_csum;
-}
-EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats);
-
-void qeth_core_get_strings(struct net_device *dev, u32 stringset, u8 *data)
-{
-       switch (stringset) {
-       case ETH_SS_STATS:
-               memcpy(data, &qeth_ethtool_stats_keys,
-                       sizeof(qeth_ethtool_stats_keys));
-               break;
-       default:
-               WARN_ON(1);
-               break;
-       }
-}
-EXPORT_SYMBOL_GPL(qeth_core_get_strings);
-
-void qeth_core_get_drvinfo(struct net_device *dev,
-               struct ethtool_drvinfo *info)
-{
-       struct qeth_card *card = dev->ml_priv;
-
-       strlcpy(info->driver, IS_LAYER2(card) ? "qeth_l2" : "qeth_l3",
-               sizeof(info->driver));
-       strlcpy(info->version, "1.0", sizeof(info->version));
-       strlcpy(info->fw_version, card->info.mcl_level,
-               sizeof(info->fw_version));
-       snprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s",
-                CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card));
-}
-EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo);
-
-/* Helper function to fill 'advertising' and 'supported' which are the same. */
-/* Autoneg and full-duplex are supported and advertised unconditionally.     */
-/* Always advertise and support all speeds up to specified, and only one     */
-/* specified port type.                                                             */
-static void qeth_set_cmd_adv_sup(struct ethtool_link_ksettings *cmd,
-                               int maxspeed, int porttype)
-{
-       ethtool_link_ksettings_zero_link_mode(cmd, supported);
-       ethtool_link_ksettings_zero_link_mode(cmd, advertising);
-       ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising);
-
-       ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
-       ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
-
-       switch (porttype) {
-       case PORT_TP:
-               ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
-               break;
-       case PORT_FIBRE:
-               ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE);
-               break;
-       default:
-               ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
-               WARN_ON_ONCE(1);
-       }
-
-       /* partially does fall through, to also select lower speeds */
-       switch (maxspeed) {
-       case SPEED_25000:
-               ethtool_link_ksettings_add_link_mode(cmd, supported,
-                                                    25000baseSR_Full);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising,
-                                                    25000baseSR_Full);
-               break;
-       case SPEED_10000:
-               ethtool_link_ksettings_add_link_mode(cmd, supported,
-                                                    10000baseT_Full);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising,
-                                                    10000baseT_Full);
-       case SPEED_1000:
-               ethtool_link_ksettings_add_link_mode(cmd, supported,
-                                                    1000baseT_Full);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising,
-                                                    1000baseT_Full);
-               ethtool_link_ksettings_add_link_mode(cmd, supported,
-                                                    1000baseT_Half);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising,
-                                                    1000baseT_Half);
-       case SPEED_100:
-               ethtool_link_ksettings_add_link_mode(cmd, supported,
-                                                    100baseT_Full);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising,
-                                                    100baseT_Full);
-               ethtool_link_ksettings_add_link_mode(cmd, supported,
-                                                    100baseT_Half);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising,
-                                                    100baseT_Half);
-       case SPEED_10:
-               ethtool_link_ksettings_add_link_mode(cmd, supported,
-                                                    10baseT_Full);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising,
-                                                    10baseT_Full);
-               ethtool_link_ksettings_add_link_mode(cmd, supported,
-                                                    10baseT_Half);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising,
-                                                    10baseT_Half);
-               /* end fallthrough */
-               break;
-       default:
-               ethtool_link_ksettings_add_link_mode(cmd, supported,
-                                                    10baseT_Full);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising,
-                                                    10baseT_Full);
-               ethtool_link_ksettings_add_link_mode(cmd, supported,
-                                                    10baseT_Half);
-               ethtool_link_ksettings_add_link_mode(cmd, advertising,
-                                                    10baseT_Half);
-               WARN_ON_ONCE(1);
-       }
-}
-
-int qeth_core_ethtool_get_link_ksettings(struct net_device *netdev,
-               struct ethtool_link_ksettings *cmd)
-{
-       struct qeth_card *card = netdev->ml_priv;
-       enum qeth_link_types link_type;
-       struct carrier_info carrier_info;
-       int rc;
-
-       if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))
-               link_type = QETH_LINK_TYPE_10GBIT_ETH;
-       else
-               link_type = card->info.link_type;
-
-       cmd->base.duplex = DUPLEX_FULL;
-       cmd->base.autoneg = AUTONEG_ENABLE;
-       cmd->base.phy_address = 0;
-       cmd->base.mdio_support = 0;
-       cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
-       cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID;
-
-       switch (link_type) {
-       case QETH_LINK_TYPE_FAST_ETH:
-       case QETH_LINK_TYPE_LANE_ETH100:
-               cmd->base.speed = SPEED_100;
-               cmd->base.port = PORT_TP;
-               break;
-       case QETH_LINK_TYPE_GBIT_ETH:
-       case QETH_LINK_TYPE_LANE_ETH1000:
-               cmd->base.speed = SPEED_1000;
-               cmd->base.port = PORT_FIBRE;
-               break;
-       case QETH_LINK_TYPE_10GBIT_ETH:
-               cmd->base.speed = SPEED_10000;
-               cmd->base.port = PORT_FIBRE;
-               break;
-       case QETH_LINK_TYPE_25GBIT_ETH:
-               cmd->base.speed = SPEED_25000;
-               cmd->base.port = PORT_FIBRE;
-               break;
-       default:
-               cmd->base.speed = SPEED_10;
-               cmd->base.port = PORT_TP;
-       }
-       qeth_set_cmd_adv_sup(cmd, cmd->base.speed, cmd->base.port);
-
-       /* Check if we can obtain more accurate information.     */
-       /* If QUERY_CARD_INFO command is not supported or fails, */
-       /* just return the heuristics that was filled above.     */
-       rc = qeth_query_card_info(card, &carrier_info);
-       if (rc == -EOPNOTSUPP) /* for old hardware, return heuristic */
-               return 0;
-       if (rc) /* report error from the hardware operation */
-               return rc;
-       /* on success, fill in the information got from the hardware */
-
-       netdev_dbg(netdev,
-       "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n",
-                       carrier_info.card_type,
-                       carrier_info.port_mode,
-                       carrier_info.port_speed);
-
-       /* Update attributes for which we've obtained more authoritative */
-       /* information, leave the rest the way they where filled above.  */
-       switch (carrier_info.card_type) {
-       case CARD_INFO_TYPE_1G_COPPER_A:
-       case CARD_INFO_TYPE_1G_COPPER_B:
-               cmd->base.port = PORT_TP;
-               qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port);
-               break;
-       case CARD_INFO_TYPE_1G_FIBRE_A:
-       case CARD_INFO_TYPE_1G_FIBRE_B:
-               cmd->base.port = PORT_FIBRE;
-               qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port);
-               break;
-       case CARD_INFO_TYPE_10G_FIBRE_A:
-       case CARD_INFO_TYPE_10G_FIBRE_B:
-               cmd->base.port = PORT_FIBRE;
-               qeth_set_cmd_adv_sup(cmd, SPEED_10000, cmd->base.port);
-               break;
-       }
-
-       switch (carrier_info.port_mode) {
-       case CARD_INFO_PORTM_FULLDUPLEX:
-               cmd->base.duplex = DUPLEX_FULL;
-               break;
-       case CARD_INFO_PORTM_HALFDUPLEX:
-               cmd->base.duplex = DUPLEX_HALF;
-               break;
-       }
-
-       switch (carrier_info.port_speed) {
-       case CARD_INFO_PORTS_10M:
-               cmd->base.speed = SPEED_10;
-               break;
-       case CARD_INFO_PORTS_100M:
-               cmd->base.speed = SPEED_100;
-               break;
-       case CARD_INFO_PORTS_1G:
-               cmd->base.speed = SPEED_1000;
-               break;
-       case CARD_INFO_PORTS_10G:
-               cmd->base.speed = SPEED_10000;
-               break;
-       case CARD_INFO_PORTS_25G:
-               cmd->base.speed = SPEED_25000;
-               break;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_link_ksettings);
-
-/* Callback to handle checksum offload command reply from OSA card.
- * Verify that required features have been enabled on the card.
- * Return error in hdr->return_code as this value is checked by caller.
- *
- * Always returns zero to indicate no further messages from the OSA card.
- */
-static int qeth_ipa_checksum_run_cmd_cb(struct qeth_card *card,
-                                       struct qeth_reply *reply,
-                                       unsigned long data)
+static int qeth_start_csum_cb(struct qeth_card *card, struct qeth_reply *reply,
+                             unsigned long data)
 {
        struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
-       struct qeth_checksum_cmd *chksum_cb =
-                               (struct qeth_checksum_cmd *)reply->param;
+       u32 *features = reply->param;
 
-       QETH_CARD_TEXT(card, 4, "chkdoccb");
        if (qeth_setassparms_inspect_rc(cmd))
-               return 0;
+               return -EIO;
 
-       memset(chksum_cb, 0, sizeof(*chksum_cb));
-       if (cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) {
-               chksum_cb->supported =
-                               cmd->data.setassparms.data.chksum.supported;
-               QETH_CARD_TEXT_(card, 3, "strt:%x", chksum_cb->supported);
-       }
-       if (cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_ENABLE) {
-               chksum_cb->supported =
-                               cmd->data.setassparms.data.chksum.supported;
-               chksum_cb->enabled =
-                               cmd->data.setassparms.data.chksum.enabled;
-               QETH_CARD_TEXT_(card, 3, "supp:%x", chksum_cb->supported);
-               QETH_CARD_TEXT_(card, 3, "enab:%x", chksum_cb->enabled);
-       }
+       *features = cmd->data.setassparms.data.flags_32bit;
        return 0;
 }
 
-/* Send command to OSA card and check results. */
-static int qeth_ipa_checksum_run_cmd(struct qeth_card *card,
-                                    enum qeth_ipa_funcs ipa_func,
-                                    __u16 cmd_code, long data,
-                                    struct qeth_checksum_cmd *chksum_cb,
-                                    enum qeth_prot_versions prot)
+static int qeth_set_csum_off(struct qeth_card *card, enum qeth_ipa_funcs cstype,
+                            enum qeth_prot_versions prot)
 {
-       struct qeth_cmd_buffer *iob;
-
-       QETH_CARD_TEXT(card, 4, "chkdocmd");
-       iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code,
-                                      sizeof(__u32), prot);
-       if (!iob)
-               return -ENOMEM;
-
-       __ipa_cmd(iob)->data.setassparms.data.flags_32bit = (__u32) data;
-       return qeth_send_ipa_cmd(card, iob, qeth_ipa_checksum_run_cmd_cb,
-                                chksum_cb);
+       return qeth_send_simple_setassparms_prot(card, cstype,
+                                                IPA_CMD_ASS_STOP, 0, prot);
 }
 
-static int qeth_send_checksum_on(struct qeth_card *card, int cstype,
-                                enum qeth_prot_versions prot)
+static int qeth_set_csum_on(struct qeth_card *card, enum qeth_ipa_funcs cstype,
+                           enum qeth_prot_versions prot)
 {
        u32 required_features = QETH_IPA_CHECKSUM_UDP | QETH_IPA_CHECKSUM_TCP;
-       struct qeth_checksum_cmd chksum_cb;
+       struct qeth_cmd_buffer *iob;
+       struct qeth_ipa_caps caps;
+       u32 features;
        int rc;
 
-       if (prot == QETH_PROT_IPV4)
+       /* some L3 HW requires combined L3+L4 csum offload: */
+       if (IS_LAYER3(card) && prot == QETH_PROT_IPV4 &&
+           cstype == IPA_OUTBOUND_CHECKSUM)
                required_features |= QETH_IPA_CHECKSUM_IP_HDR;
-       rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_START, 0,
-                                      &chksum_cb, prot);
-       if (!rc) {
-               if ((required_features & chksum_cb.supported) !=
-                   required_features)
-                       rc = -EIO;
-               else if (!(QETH_IPA_CHECKSUM_LP2LP & chksum_cb.supported) &&
-                        cstype == IPA_INBOUND_CHECKSUM)
-                       dev_warn(&card->gdev->dev,
-                                "Hardware checksumming is performed only if %s and its peer use different OSA Express 3 ports\n",
-                                QETH_CARD_IFNAME(card));
-       }
-       if (rc) {
-               qeth_send_simple_setassparms_prot(card, cstype,
-                                                 IPA_CMD_ASS_STOP, 0, prot);
-               dev_warn(&card->gdev->dev,
-                        "Starting HW IPv%d checksumming for %s failed, using SW checksumming\n",
-                        prot, QETH_CARD_IFNAME(card));
+
+       iob = qeth_get_setassparms_cmd(card, cstype, IPA_CMD_ASS_START, 0,
+                                      prot);
+       if (!iob)
+               return -ENOMEM;
+
+       rc = qeth_send_ipa_cmd(card, iob, qeth_start_csum_cb, &features);
+       if (rc)
                return rc;
+
+       if ((required_features & features) != required_features) {
+               qeth_set_csum_off(card, cstype, prot);
+               return -EOPNOTSUPP;
        }
-       rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_ENABLE,
-                                      chksum_cb.supported, &chksum_cb,
+
+       iob = qeth_get_setassparms_cmd(card, cstype, IPA_CMD_ASS_ENABLE, 4,
                                       prot);
-       if (!rc) {
-               if ((required_features & chksum_cb.enabled) !=
-                   required_features)
-                       rc = -EIO;
+       if (!iob) {
+               qeth_set_csum_off(card, cstype, prot);
+               return -ENOMEM;
        }
+
+       if (features & QETH_IPA_CHECKSUM_LP2LP)
+               required_features |= QETH_IPA_CHECKSUM_LP2LP;
+       __ipa_cmd(iob)->data.setassparms.data.flags_32bit = required_features;
+       rc = qeth_send_ipa_cmd(card, iob, qeth_setassparms_get_caps_cb, &caps);
        if (rc) {
-               qeth_send_simple_setassparms_prot(card, cstype,
-                                                 IPA_CMD_ASS_STOP, 0, prot);
-               dev_warn(&card->gdev->dev,
-                        "Enabling HW IPv%d checksumming for %s failed, using SW checksumming\n",
-                        prot, QETH_CARD_IFNAME(card));
+               qeth_set_csum_off(card, cstype, prot);
                return rc;
        }
 
+       if (!qeth_ipa_caps_supported(&caps, required_features) ||
+           !qeth_ipa_caps_enabled(&caps, required_features)) {
+               qeth_set_csum_off(card, cstype, prot);
+               return -EOPNOTSUPP;
+       }
+
        dev_info(&card->gdev->dev, "HW Checksumming (%sbound IPv%d) enabled\n",
                 cstype == IPA_INBOUND_CHECKSUM ? "in" : "out", prot);
+       if (!qeth_ipa_caps_enabled(&caps, QETH_IPA_CHECKSUM_LP2LP) &&
+           cstype == IPA_OUTBOUND_CHECKSUM)
+               dev_warn(&card->gdev->dev,
+                        "Hardware checksumming is performed only if %s and its peer use different OSA Express 3 ports\n",
+                        QETH_CARD_IFNAME(card));
        return 0;
 }
 
 static int qeth_set_ipa_csum(struct qeth_card *card, bool on, int cstype,
                             enum qeth_prot_versions prot)
 {
-       int rc = (on) ? qeth_send_checksum_on(card, cstype, prot)
-                     : qeth_send_simple_setassparms_prot(card, cstype,
-                                                         IPA_CMD_ASS_STOP, 0,
-                                                         prot);
-       return rc ? -EIO : 0;
+       return on ? qeth_set_csum_on(card, cstype, prot) :
+                   qeth_set_csum_off(card, cstype, prot);
 }
 
 static int qeth_start_tso_cb(struct qeth_card *card, struct qeth_reply *reply,
@@ -6393,7 +5983,7 @@ static int qeth_start_tso_cb(struct qeth_card *card, struct qeth_reply *reply,
        struct qeth_tso_start_data *tso_data = reply->param;
 
        if (qeth_setassparms_inspect_rc(cmd))
-               return 0;
+               return -EIO;
 
        tso_data->mss = cmd->data.setassparms.data.tso.mss;
        tso_data->supported = cmd->data.setassparms.data.tso.supported;
@@ -6459,10 +6049,7 @@ static int qeth_set_tso_on(struct qeth_card *card,
 static int qeth_set_ipa_tso(struct qeth_card *card, bool on,
                            enum qeth_prot_versions prot)
 {
-       int rc = on ? qeth_set_tso_on(card, prot) :
-                     qeth_set_tso_off(card, prot);
-
-       return rc ? -EIO : 0;
+       return on ? qeth_set_tso_on(card, prot) : qeth_set_tso_off(card, prot);
 }
 
 static int qeth_set_ipa_rx_csum(struct qeth_card *card, bool on)
@@ -6611,6 +6198,33 @@ netdev_features_t qeth_features_check(struct sk_buff *skb,
 }
 EXPORT_SYMBOL_GPL(qeth_features_check);
 
+void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+       struct qeth_card *card = dev->ml_priv;
+       struct qeth_qdio_out_q *queue;
+       unsigned int i;
+
+       QETH_CARD_TEXT(card, 5, "getstat");
+
+       stats->rx_packets = card->stats.rx_packets;
+       stats->rx_bytes = card->stats.rx_bytes;
+       stats->rx_errors = card->stats.rx_errors;
+       stats->rx_dropped = card->stats.rx_dropped;
+       stats->multicast = card->stats.rx_multicast;
+       stats->tx_errors = card->stats.tx_errors;
+
+       for (i = 0; i < card->qdio.no_out_queues; i++) {
+               queue = card->qdio.out_qs[i];
+
+               stats->tx_packets += queue->stats.tx_packets;
+               stats->tx_bytes += queue->stats.tx_bytes;
+               stats->tx_errors += queue->stats.tx_errors;
+               stats->tx_dropped += queue->stats.tx_dropped;
+               stats->tx_carrier_errors += queue->stats.tx_carrier_errors;
+       }
+}
+EXPORT_SYMBOL_GPL(qeth_get_stats64);
+
 int qeth_open(struct net_device *dev)
 {
        struct qeth_card *card = dev->ml_priv;
index 16fc51a..e3f4866 100644 (file)
@@ -125,24 +125,13 @@ unsigned char DM_ACT[] = {
 
 unsigned char IPA_PDU_HEADER[] = {
        0x00, 0xe0, 0x00, 0x00,  0x77, 0x77, 0x77, 0x77,
-       0x00, 0x00, 0x00, 0x14,  0x00, 0x00,
-               (IPA_PDU_HEADER_SIZE+sizeof(struct qeth_ipa_cmd)) / 256,
-               (IPA_PDU_HEADER_SIZE+sizeof(struct qeth_ipa_cmd)) % 256,
+       0x00, 0x00, 0x00, 0x14,  0x00, 0x00, 0x00, 0x00,
        0x10, 0x00, 0x00, 0x01,  0x00, 0x00, 0x00, 0x00,
        0xc1, 0x03, 0x00, 0x01,  0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00,  0x00, 0x24,
-               sizeof(struct qeth_ipa_cmd) / 256,
-               sizeof(struct qeth_ipa_cmd) % 256,
-       0x00,
-               sizeof(struct qeth_ipa_cmd) / 256,
-               sizeof(struct qeth_ipa_cmd) % 256,
-       0x05,
-       0x77, 0x77, 0x77, 0x77,
+       0x00, 0x00, 0x00, 0x00,  0x00, 0x24, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x05,  0x77, 0x77, 0x77, 0x77,
        0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
-       0x01, 0x00,
-               sizeof(struct qeth_ipa_cmd) / 256,
-               sizeof(struct qeth_ipa_cmd) % 256,
-       0x00, 0x00, 0x00, 0x40,
+       0x01, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x40,
 };
 
 struct ipa_rc_msg {
@@ -212,12 +201,10 @@ static const struct ipa_rc_msg qeth_ipa_rc_msg[] = {
        {IPA_RC_LAN_OFFLINE,            "STRTLAN_LAN_DISABLED - LAN offline"},
        {IPA_RC_VEPA_TO_VEB_TRANSITION, "Adj. switch disabled port mode RR"},
        {IPA_RC_INVALID_IP_VERSION2,    "Invalid IP version"},
-       {IPA_RC_ENOMEM,                 "Memory problem"},
+       /* default for qeth_get_ipa_msg(): */
        {IPA_RC_FFFF,                   "Unknown Error"}
 };
 
-
-
 const char *qeth_get_ipa_msg(enum qeth_ipa_return_codes rc)
 {
        int x;
index c7fb14a..f8c5d4a 100644 (file)
@@ -21,8 +21,6 @@
 extern unsigned char IPA_PDU_HEADER[];
 #define QETH_IPA_CMD_DEST_ADDR(buffer) (buffer + 0x2c)
 
-#define IPA_CMD_LENGTH (IPA_PDU_HEADER_SIZE + sizeof(struct qeth_ipa_cmd))
-
 #define QETH_SEQ_NO_LENGTH     4
 #define QETH_MPC_TOKEN_LENGTH  4
 #define QETH_MCL_LENGTH                4
@@ -83,6 +81,7 @@ enum qeth_card_types {
 #define IS_OSD(card)   ((card)->info.type == QETH_CARD_TYPE_OSD)
 #define IS_OSM(card)   ((card)->info.type == QETH_CARD_TYPE_OSM)
 #define IS_OSN(card)   ((card)->info.type == QETH_CARD_TYPE_OSN)
+#define IS_OSX(card)   ((card)->info.type == QETH_CARD_TYPE_OSX)
 #define IS_VM_NIC(card)        ((card)->info.guestlan)
 
 #define QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE 0x18
@@ -231,7 +230,6 @@ enum qeth_ipa_return_codes {
        IPA_RC_LAN_OFFLINE              = 0xe080,
        IPA_RC_VEPA_TO_VEB_TRANSITION   = 0xe090,
        IPA_RC_INVALID_IP_VERSION2      = 0xf001,
-       IPA_RC_ENOMEM                   = 0xfffe,
        IPA_RC_FFFF                     = 0xffff
 };
 /* for VNIC Characteristics */
@@ -419,12 +417,6 @@ enum qeth_ipa_checksum_bits {
        QETH_IPA_CHECKSUM_LP2LP         = 0x0020
 };
 
-/* IPA Assist checksum offload reply layout. */
-struct qeth_checksum_cmd {
-       __u32 supported;
-       __u32 enabled;
-} __packed;
-
 enum qeth_ipa_large_send_caps {
        QETH_IPA_LARGE_SEND_TCP         = 0x00000001,
 };
@@ -440,7 +432,6 @@ struct qeth_ipacmd_setassparms {
        union {
                __u32 flags_32bit;
                struct qeth_ipa_caps caps;
-               struct qeth_checksum_cmd chksum;
                struct qeth_arp_cache_entry arp_entry;
                struct qeth_arp_query_data query_arp;
                struct qeth_tso_start_data tso;
@@ -834,15 +825,10 @@ enum qeth_ipa_arp_return_codes {
 extern const char *qeth_get_ipa_msg(enum qeth_ipa_return_codes rc);
 extern const char *qeth_get_ipa_cmd_name(enum qeth_ipa_cmds cmd);
 
-#define QETH_SETASS_BASE_LEN (IPA_PDU_HEADER_SIZE + \
-                             sizeof(struct qeth_ipacmd_hdr) + \
-                             sizeof(struct qeth_ipacmd_setassparms_hdr))
 #define QETH_SETADP_BASE_LEN (sizeof(struct qeth_ipacmd_hdr) + \
                              sizeof(struct qeth_ipacmd_setadpparms_hdr))
 #define QETH_SNMP_SETADP_CMDLENGTH 16
 
-#define QETH_ARP_DATA_SIZE 3968
-#define QETH_ARP_CMD_LEN (QETH_ARP_DATA_SIZE + 8)
 /* Helper functions */
 #define IS_IPA_REPLY(cmd) ((cmd->hdr.initiator == IPA_CMD_INITIATOR_HOST) || \
                           (cmd->hdr.initiator == IPA_CMD_INITIATOR_OSA_REPLY))
index 30f6160..8b223cc 100644 (file)
@@ -316,7 +316,7 @@ static ssize_t qeth_dev_recover_store(struct device *dev,
        if (!card)
                return -EINVAL;
 
-       if (card->state != CARD_STATE_UP)
+       if (!qeth_card_hw_is_reachable(card))
                return -EPERM;
 
        i = simple_strtoul(buf, &tmp, 16);
@@ -336,35 +336,36 @@ static ssize_t qeth_dev_performance_stats_show(struct device *dev,
        if (!card)
                return -EINVAL;
 
-       return sprintf(buf, "%i\n", card->options.performance_stats ? 1:0);
+       return sprintf(buf, "1\n");
 }
 
 static ssize_t qeth_dev_performance_stats_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
        struct qeth_card *card = dev_get_drvdata(dev);
-       char *tmp;
-       int i, rc = 0;
+       struct qeth_qdio_out_q *queue;
+       unsigned int i;
+       bool reset;
+       int rc;
 
        if (!card)
                return -EINVAL;
 
-       mutex_lock(&card->conf_mutex);
-       i = simple_strtoul(buf, &tmp, 16);
-       if ((i == 0) || (i == 1)) {
-               if (i == card->options.performance_stats)
-                       goto out;
-               card->options.performance_stats = i;
-               if (i == 0)
-                       memset(&card->perf_stats, 0,
-                               sizeof(struct qeth_perf_stats));
-               card->perf_stats.initial_rx_packets = card->stats.rx_packets;
-               card->perf_stats.initial_tx_packets = card->stats.tx_packets;
-       } else
-               rc = -EINVAL;
-out:
-       mutex_unlock(&card->conf_mutex);
-       return rc ? rc : count;
+       rc = kstrtobool(buf, &reset);
+       if (rc)
+               return rc;
+
+       if (reset) {
+               memset(&card->stats, 0, sizeof(card->stats));
+               for (i = 0; i < card->qdio.no_out_queues; i++) {
+                       queue = card->qdio.out_qs[i];
+                       if (!queue)
+                               break;
+                       memset(&queue->stats, 0, sizeof(queue->stats));
+               }
+       }
+
+       return count;
 }
 
 static DEVICE_ATTR(performance_stats, 0644, qeth_dev_performance_stats_show,
diff --git a/drivers/s390/net/qeth_ethtool.c b/drivers/s390/net/qeth_ethtool.c
new file mode 100644 (file)
index 0000000..93a53fe
--- /dev/null
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright IBM Corp. 2018
+ */
+
+#define KMSG_COMPONENT "qeth"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/ethtool.h>
+#include "qeth_core.h"
+
+
+#define QETH_TXQ_STAT(_name, _stat) { \
+       .name = _name, \
+       .offset = offsetof(struct qeth_out_q_stats, _stat) \
+}
+
+#define QETH_CARD_STAT(_name, _stat) { \
+       .name = _name, \
+       .offset = offsetof(struct qeth_card_stats, _stat) \
+}
+
+struct qeth_stats {
+       char name[ETH_GSTRING_LEN];
+       unsigned int offset;
+};
+
+static const struct qeth_stats txq_stats[] = {
+       QETH_TXQ_STAT("IO buffers", bufs),
+       QETH_TXQ_STAT("IO buffer elements", buf_elements),
+       QETH_TXQ_STAT("packed IO buffers", bufs_pack),
+       QETH_TXQ_STAT("skbs", tx_packets),
+       QETH_TXQ_STAT("packed skbs", skbs_pack),
+       QETH_TXQ_STAT("SG skbs", skbs_sg),
+       QETH_TXQ_STAT("HW csum skbs", skbs_csum),
+       QETH_TXQ_STAT("TSO skbs", skbs_tso),
+       QETH_TXQ_STAT("linearized skbs", skbs_linearized),
+       QETH_TXQ_STAT("linearized+error skbs", skbs_linearized_fail),
+       QETH_TXQ_STAT("TSO bytes", tso_bytes),
+       QETH_TXQ_STAT("Packing mode switches", packing_mode_switch),
+};
+
+static const struct qeth_stats card_stats[] = {
+       QETH_CARD_STAT("rx0 IO buffers", rx_bufs),
+       QETH_CARD_STAT("rx0 HW csum skbs", rx_skb_csum),
+       QETH_CARD_STAT("rx0 SG skbs", rx_sg_skbs),
+       QETH_CARD_STAT("rx0 SG page frags", rx_sg_frags),
+       QETH_CARD_STAT("rx0 SG page allocs", rx_sg_alloc_page),
+};
+
+#define TXQ_STATS_LEN  ARRAY_SIZE(txq_stats)
+#define CARD_STATS_LEN ARRAY_SIZE(card_stats)
+
+static void qeth_add_stat_data(u64 **dst, void *src,
+                              const struct qeth_stats stats[],
+                              unsigned int size)
+{
+       unsigned int i;
+       char *stat;
+
+       for (i = 0; i < size; i++) {
+               stat = (char *)src + stats[i].offset;
+               **dst = *(u64 *)stat;
+               (*dst)++;
+       }
+}
+
+static void qeth_add_stat_strings(u8 **data, const char *prefix,
+                                 const struct qeth_stats stats[],
+                                 unsigned int size)
+{
+       unsigned int i;
+
+       for (i = 0; i < size; i++) {
+               snprintf(*data, ETH_GSTRING_LEN, "%s%s", prefix, stats[i].name);
+               *data += ETH_GSTRING_LEN;
+       }
+}
+
+static int qeth_get_sset_count(struct net_device *dev, int stringset)
+{
+       struct qeth_card *card = dev->ml_priv;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               return CARD_STATS_LEN +
+                      card->qdio.no_out_queues * TXQ_STATS_LEN;
+       default:
+               return -EINVAL;
+       }
+}
+
+static void qeth_get_ethtool_stats(struct net_device *dev,
+                                  struct ethtool_stats *stats, u64 *data)
+{
+       struct qeth_card *card = dev->ml_priv;
+       unsigned int i;
+
+       qeth_add_stat_data(&data, &card->stats, card_stats, CARD_STATS_LEN);
+       for (i = 0; i < card->qdio.no_out_queues; i++)
+               qeth_add_stat_data(&data, &card->qdio.out_qs[i]->stats,
+                                  txq_stats, TXQ_STATS_LEN);
+}
+
+static void qeth_get_ringparam(struct net_device *dev,
+                              struct ethtool_ringparam *param)
+{
+       struct qeth_card *card = dev->ml_priv;
+
+       param->rx_max_pending = QDIO_MAX_BUFFERS_PER_Q;
+       param->rx_mini_max_pending = 0;
+       param->rx_jumbo_max_pending = 0;
+       param->tx_max_pending = QDIO_MAX_BUFFERS_PER_Q;
+
+       param->rx_pending = card->qdio.in_buf_pool.buf_count;
+       param->rx_mini_pending = 0;
+       param->rx_jumbo_pending = 0;
+       param->tx_pending = QDIO_MAX_BUFFERS_PER_Q;
+}
+
+static void qeth_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+       struct qeth_card *card = dev->ml_priv;
+       char prefix[ETH_GSTRING_LEN] = "";
+       unsigned int i;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               qeth_add_stat_strings(&data, prefix, card_stats,
+                                     CARD_STATS_LEN);
+               for (i = 0; i < card->qdio.no_out_queues; i++) {
+                       snprintf(prefix, ETH_GSTRING_LEN, "tx%u ", i);
+                       qeth_add_stat_strings(&data, prefix, txq_stats,
+                                             TXQ_STATS_LEN);
+               }
+               break;
+       default:
+               WARN_ON(1);
+               break;
+       }
+}
+
+static void qeth_get_drvinfo(struct net_device *dev,
+                            struct ethtool_drvinfo *info)
+{
+       struct qeth_card *card = dev->ml_priv;
+
+       strlcpy(info->driver, IS_LAYER2(card) ? "qeth_l2" : "qeth_l3",
+               sizeof(info->driver));
+       strlcpy(info->version, "1.0", sizeof(info->version));
+       strlcpy(info->fw_version, card->info.mcl_level,
+               sizeof(info->fw_version));
+       snprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s",
+                CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card));
+}
+
+/* Helper function to fill 'advertising' and 'supported' which are the same. */
+/* Autoneg and full-duplex are supported and advertised unconditionally.     */
+/* Always advertise and support all speeds up to specified, and only one     */
+/* specified port type.                                                             */
+static void qeth_set_cmd_adv_sup(struct ethtool_link_ksettings *cmd,
+                               int maxspeed, int porttype)
+{
+       ethtool_link_ksettings_zero_link_mode(cmd, supported);
+       ethtool_link_ksettings_zero_link_mode(cmd, advertising);
+       ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising);
+
+       ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
+       ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
+
+       switch (porttype) {
+       case PORT_TP:
+               ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
+               break;
+       case PORT_FIBRE:
+               ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE);
+               break;
+       default:
+               ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
+               WARN_ON_ONCE(1);
+       }
+
+       /* partially does fall through, to also select lower speeds */
+       switch (maxspeed) {
+       case SPEED_25000:
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    25000baseSR_Full);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    25000baseSR_Full);
+               break;
+       case SPEED_10000:
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    10000baseT_Full);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    10000baseT_Full);
+               /* fall through */
+       case SPEED_1000:
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    1000baseT_Full);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    1000baseT_Full);
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    1000baseT_Half);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    1000baseT_Half);
+               /* fall through */
+       case SPEED_100:
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    100baseT_Full);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    100baseT_Full);
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    100baseT_Half);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    100baseT_Half);
+               /* fall through */
+       case SPEED_10:
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    10baseT_Full);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    10baseT_Full);
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    10baseT_Half);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    10baseT_Half);
+               break;
+       default:
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    10baseT_Full);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    10baseT_Full);
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    10baseT_Half);
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    10baseT_Half);
+               WARN_ON_ONCE(1);
+       }
+}
+
+
+static int qeth_get_link_ksettings(struct net_device *netdev,
+                                  struct ethtool_link_ksettings *cmd)
+{
+       struct qeth_card *card = netdev->ml_priv;
+       enum qeth_link_types link_type;
+       struct carrier_info carrier_info;
+       int rc;
+
+       if (IS_IQD(card) || IS_VM_NIC(card))
+               link_type = QETH_LINK_TYPE_10GBIT_ETH;
+       else
+               link_type = card->info.link_type;
+
+       cmd->base.duplex = DUPLEX_FULL;
+       cmd->base.autoneg = AUTONEG_ENABLE;
+       cmd->base.phy_address = 0;
+       cmd->base.mdio_support = 0;
+       cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
+       cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID;
+
+       switch (link_type) {
+       case QETH_LINK_TYPE_FAST_ETH:
+       case QETH_LINK_TYPE_LANE_ETH100:
+               cmd->base.speed = SPEED_100;
+               cmd->base.port = PORT_TP;
+               break;
+       case QETH_LINK_TYPE_GBIT_ETH:
+       case QETH_LINK_TYPE_LANE_ETH1000:
+               cmd->base.speed = SPEED_1000;
+               cmd->base.port = PORT_FIBRE;
+               break;
+       case QETH_LINK_TYPE_10GBIT_ETH:
+               cmd->base.speed = SPEED_10000;
+               cmd->base.port = PORT_FIBRE;
+               break;
+       case QETH_LINK_TYPE_25GBIT_ETH:
+               cmd->base.speed = SPEED_25000;
+               cmd->base.port = PORT_FIBRE;
+               break;
+       default:
+               cmd->base.speed = SPEED_10;
+               cmd->base.port = PORT_TP;
+       }
+       qeth_set_cmd_adv_sup(cmd, cmd->base.speed, cmd->base.port);
+
+       /* Check if we can obtain more accurate information.     */
+       /* If QUERY_CARD_INFO command is not supported or fails, */
+       /* just return the heuristics that was filled above.     */
+       rc = qeth_query_card_info(card, &carrier_info);
+       if (rc == -EOPNOTSUPP) /* for old hardware, return heuristic */
+               return 0;
+       if (rc) /* report error from the hardware operation */
+               return rc;
+       /* on success, fill in the information got from the hardware */
+
+       netdev_dbg(netdev,
+       "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n",
+                       carrier_info.card_type,
+                       carrier_info.port_mode,
+                       carrier_info.port_speed);
+
+       /* Update attributes for which we've obtained more authoritative */
+       /* information, leave the rest the way they where filled above.  */
+       switch (carrier_info.card_type) {
+       case CARD_INFO_TYPE_1G_COPPER_A:
+       case CARD_INFO_TYPE_1G_COPPER_B:
+               cmd->base.port = PORT_TP;
+               qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port);
+               break;
+       case CARD_INFO_TYPE_1G_FIBRE_A:
+       case CARD_INFO_TYPE_1G_FIBRE_B:
+               cmd->base.port = PORT_FIBRE;
+               qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port);
+               break;
+       case CARD_INFO_TYPE_10G_FIBRE_A:
+       case CARD_INFO_TYPE_10G_FIBRE_B:
+               cmd->base.port = PORT_FIBRE;
+               qeth_set_cmd_adv_sup(cmd, SPEED_10000, cmd->base.port);
+               break;
+       }
+
+       switch (carrier_info.port_mode) {
+       case CARD_INFO_PORTM_FULLDUPLEX:
+               cmd->base.duplex = DUPLEX_FULL;
+               break;
+       case CARD_INFO_PORTM_HALFDUPLEX:
+               cmd->base.duplex = DUPLEX_HALF;
+               break;
+       }
+
+       switch (carrier_info.port_speed) {
+       case CARD_INFO_PORTS_10M:
+               cmd->base.speed = SPEED_10;
+               break;
+       case CARD_INFO_PORTS_100M:
+               cmd->base.speed = SPEED_100;
+               break;
+       case CARD_INFO_PORTS_1G:
+               cmd->base.speed = SPEED_1000;
+               break;
+       case CARD_INFO_PORTS_10G:
+               cmd->base.speed = SPEED_10000;
+               break;
+       case CARD_INFO_PORTS_25G:
+               cmd->base.speed = SPEED_25000;
+               break;
+       }
+
+       return 0;
+}
+
+const struct ethtool_ops qeth_ethtool_ops = {
+       .get_link = ethtool_op_get_link,
+       .get_ringparam = qeth_get_ringparam,
+       .get_strings = qeth_get_strings,
+       .get_ethtool_stats = qeth_get_ethtool_stats,
+       .get_sset_count = qeth_get_sset_count,
+       .get_drvinfo = qeth_get_drvinfo,
+       .get_link_ksettings = qeth_get_link_ksettings,
+};
+
+const struct ethtool_ops qeth_osn_ethtool_ops = {
+       .get_strings = qeth_get_strings,
+       .get_ethtool_stats = qeth_get_ethtool_stats,
+       .get_sset_count = qeth_get_sset_count,
+       .get_drvinfo = qeth_get_drvinfo,
+};
index 82f50cc..2c97142 100644 (file)
@@ -35,7 +35,7 @@ static void qeth_l2_vnicc_init(struct qeth_card *card);
 static bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc,
                                          u32 *timeout);
 
-static int qeth_setdelmac_makerc(struct qeth_card *card, int retcode)
+static int qeth_l2_setdelmac_makerc(struct qeth_card *card, u16 retcode)
 {
        int rc;
 
@@ -62,9 +62,6 @@ static int qeth_setdelmac_makerc(struct qeth_card *card, int retcode)
        case IPA_RC_L2_MAC_NOT_FOUND:
                rc = -ENOENT;
                break;
-       case -ENOMEM:
-               rc = -ENOMEM;
-               break;
        default:
                rc = -EIO;
                break;
@@ -72,6 +69,15 @@ static int qeth_setdelmac_makerc(struct qeth_card *card, int retcode)
        return rc;
 }
 
+static int qeth_l2_send_setdelmac_cb(struct qeth_card *card,
+                                    struct qeth_reply *reply,
+                                    unsigned long data)
+{
+       struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
+
+       return qeth_l2_setdelmac_makerc(card, cmd->hdr.return_code);
+}
+
 static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac,
                           enum qeth_ipa_cmds ipacmd)
 {
@@ -85,8 +91,7 @@ static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac,
        cmd = __ipa_cmd(iob);
        cmd->data.setdelmac.mac_length = ETH_ALEN;
        ether_addr_copy(cmd->data.setdelmac.mac, mac);
-       return qeth_setdelmac_makerc(card, qeth_send_ipa_cmd(card, iob,
-                                          NULL, NULL));
+       return qeth_send_ipa_cmd(card, iob, qeth_l2_send_setdelmac_cb, NULL);
 }
 
 static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac)
@@ -169,9 +174,9 @@ static int qeth_l2_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
        return RTN_UNICAST;
 }
 
-static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
-                               struct sk_buff *skb, int ipv, int cast_type,
-                               unsigned int data_len)
+static void qeth_l2_fill_header(struct qeth_qdio_out_q *queue,
+                               struct qeth_hdr *hdr, struct sk_buff *skb,
+                               int ipv, int cast_type, unsigned int data_len)
 {
        struct vlan_ethhdr *veth = vlan_eth_hdr(skb);
 
@@ -183,8 +188,7 @@ static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
                if (skb->ip_summed == CHECKSUM_PARTIAL) {
                        qeth_tx_csum(skb, &hdr->hdr.l2.flags[1], ipv);
-                       if (card->options.performance_stats)
-                               card->perf_stats.tx_csum++;
+                       QETH_TXQ_STAT_INC(queue, skbs_csum);
                }
        }
 
@@ -205,7 +209,7 @@ static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
        }
 }
 
-static int qeth_setdelvlan_makerc(struct qeth_card *card, int retcode)
+static int qeth_l2_setdelvlan_makerc(struct qeth_card *card, u16 retcode)
 {
        if (retcode)
                QETH_CARD_TEXT_(card, 2, "err%04x", retcode);
@@ -221,8 +225,6 @@ static int qeth_setdelvlan_makerc(struct qeth_card *card, int retcode)
                return -ENOENT;
        case IPA_RC_L2_VLAN_ID_NOT_ALLOWED:
                return -EPERM;
-       case -ENOMEM:
-               return -ENOMEM;
        default:
                return -EIO;
        }
@@ -240,9 +242,8 @@ static int qeth_l2_send_setdelvlan_cb(struct qeth_card *card,
                                 cmd->data.setdelvlan.vlan_id,
                                 CARD_DEVID(card), cmd->hdr.return_code);
                QETH_CARD_TEXT_(card, 2, "L2VL%4x", cmd->hdr.command);
-               QETH_CARD_TEXT_(card, 2, "err%d", cmd->hdr.return_code);
        }
-       return 0;
+       return qeth_l2_setdelvlan_makerc(card, cmd->hdr.return_code);
 }
 
 static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i,
@@ -257,8 +258,7 @@ static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i,
                return -ENOMEM;
        cmd = __ipa_cmd(iob);
        cmd->data.setdelvlan.vlan_id = i;
-       return qeth_setdelvlan_makerc(card, qeth_send_ipa_cmd(card, iob,
-                                           qeth_l2_send_setdelvlan_cb, NULL));
+       return qeth_send_ipa_cmd(card, iob, qeth_l2_send_setdelvlan_cb, NULL);
 }
 
 static int qeth_l2_vlan_rx_add_vid(struct net_device *dev,
@@ -319,6 +319,8 @@ static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
                qeth_clear_cmd_buffers(&card->read);
                qeth_clear_cmd_buffers(&card->write);
        }
+
+       flush_workqueue(card->event_wq);
 }
 
 static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
@@ -366,8 +368,8 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
                }
                work_done++;
                budget--;
-               card->stats.rx_packets++;
-               card->stats.rx_bytes += len;
+               QETH_CARD_STAT_INC(card, rx_packets);
+               QETH_CARD_STAT_ADD(card, rx_bytes, len);
        }
        return work_done;
 }
@@ -391,7 +393,7 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card)
 
        if (!IS_OSN(card)) {
                rc = qeth_setadpparms_change_macaddr(card);
-               if (!rc && is_valid_ether_addr(card->dev->dev_addr))
+               if (!rc)
                        goto out;
                QETH_DBF_MESSAGE(2, "READ_MAC Assist failed on device %x: %#x\n",
                                 CARD_DEVID(card), rc);
@@ -423,7 +425,7 @@ static int qeth_l2_validate_addr(struct net_device *dev)
 {
        struct qeth_card *card = dev->ml_priv;
 
-       if (IS_OSN(card) || (card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))
+       if (card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED)
                return eth_validate_addr(dev);
 
        QETH_CARD_TEXT(card, 4, "nomacadr");
@@ -439,9 +441,7 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
 
        QETH_CARD_TEXT(card, 3, "setmac");
 
-       if (card->info.type == QETH_CARD_TYPE_OSN ||
-           card->info.type == QETH_CARD_TYPE_OSM ||
-           card->info.type == QETH_CARD_TYPE_OSX) {
+       if (IS_OSM(card) || IS_OSX(card)) {
                QETH_CARD_TEXT(card, 3, "setmcTYP");
                return -EOPNOTSUPP;
        }
@@ -535,9 +535,6 @@ static void qeth_l2_set_rx_mode(struct net_device *dev)
        int i;
        int rc;
 
-       if (card->info.type == QETH_CARD_TYPE_OSN)
-               return;
-
        QETH_CARD_TEXT(card, 3, "setmulti");
 
        spin_lock_bh(&card->mclock);
@@ -623,17 +620,13 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
        int tx_bytes = skb->len;
        int rc;
 
+       queue = qeth_get_tx_queue(card, skb, ipv, cast_type);
+
        if (card->state != CARD_STATE_UP) {
-               card->stats.tx_carrier_errors++;
+               QETH_TXQ_STAT_INC(queue, tx_carrier_errors);
                goto tx_drop;
        }
 
-       queue = qeth_get_tx_queue(card, skb, ipv, cast_type);
-
-       if (card->options.performance_stats) {
-               card->perf_stats.outbound_cnt++;
-               card->perf_stats.outbound_start_time = qeth_get_micros();
-       }
        netif_stop_queue(dev);
 
        if (IS_OSN(card))
@@ -643,11 +636,8 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
                               qeth_l2_fill_header);
 
        if (!rc) {
-               card->stats.tx_packets++;
-               card->stats.tx_bytes += tx_bytes;
-               if (card->options.performance_stats)
-                       card->perf_stats.outbound_time += qeth_get_micros() -
-                               card->perf_stats.outbound_start_time;
+               QETH_TXQ_STAT_INC(queue, tx_packets);
+               QETH_TXQ_STAT_ADD(queue, tx_bytes, tx_bytes);
                netif_wake_queue(dev);
                return NETDEV_TX_OK;
        } else if (rc == -EBUSY) {
@@ -655,8 +645,8 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
        } /* else fall through */
 
 tx_drop:
-       card->stats.tx_dropped++;
-       card->stats.tx_errors++;
+       QETH_TXQ_STAT_INC(queue, tx_dropped);
+       QETH_TXQ_STAT_INC(queue, tx_errors);
        dev_kfree_skb_any(skb);
        netif_wake_queue(dev);
        return NETDEV_TX_OK;
@@ -695,30 +685,16 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
 
        if (cgdev->state == CCWGROUP_ONLINE)
                qeth_l2_set_offline(cgdev);
+
+       cancel_work_sync(&card->close_dev_work);
        if (qeth_netdev_is_registered(card->dev))
                unregister_netdev(card->dev);
 }
 
-static const struct ethtool_ops qeth_l2_ethtool_ops = {
-       .get_link = ethtool_op_get_link,
-       .get_strings = qeth_core_get_strings,
-       .get_ethtool_stats = qeth_core_get_ethtool_stats,
-       .get_sset_count = qeth_core_get_sset_count,
-       .get_drvinfo = qeth_core_get_drvinfo,
-       .get_link_ksettings = qeth_core_ethtool_get_link_ksettings,
-};
-
-static const struct ethtool_ops qeth_l2_osn_ops = {
-       .get_strings = qeth_core_get_strings,
-       .get_ethtool_stats = qeth_core_get_ethtool_stats,
-       .get_sset_count = qeth_core_get_sset_count,
-       .get_drvinfo = qeth_core_get_drvinfo,
-};
-
 static const struct net_device_ops qeth_l2_netdev_ops = {
        .ndo_open               = qeth_open,
        .ndo_stop               = qeth_stop,
-       .ndo_get_stats          = qeth_get_stats,
+       .ndo_get_stats64        = qeth_get_stats64,
        .ndo_start_xmit         = qeth_l2_hard_start_xmit,
        .ndo_features_check     = qeth_features_check,
        .ndo_validate_addr      = qeth_l2_validate_addr,
@@ -732,20 +708,29 @@ static const struct net_device_ops qeth_l2_netdev_ops = {
        .ndo_set_features       = qeth_set_features
 };
 
+static const struct net_device_ops qeth_osn_netdev_ops = {
+       .ndo_open               = qeth_open,
+       .ndo_stop               = qeth_stop,
+       .ndo_get_stats64        = qeth_get_stats64,
+       .ndo_start_xmit         = qeth_l2_hard_start_xmit,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_tx_timeout         = qeth_tx_timeout,
+};
+
 static int qeth_l2_setup_netdev(struct qeth_card *card, bool carrier_ok)
 {
        int rc;
 
-       card->dev->priv_flags |= IFF_UNICAST_FLT;
-       card->dev->netdev_ops = &qeth_l2_netdev_ops;
-       if (card->info.type == QETH_CARD_TYPE_OSN) {
-               card->dev->ethtool_ops = &qeth_l2_osn_ops;
+       if (IS_OSN(card)) {
+               card->dev->netdev_ops = &qeth_osn_netdev_ops;
                card->dev->flags |= IFF_NOARP;
-       } else {
-               card->dev->ethtool_ops = &qeth_l2_ethtool_ops;
-               card->dev->needed_headroom = sizeof(struct qeth_hdr);
+               goto add_napi;
        }
 
+       card->dev->needed_headroom = sizeof(struct qeth_hdr);
+       card->dev->netdev_ops = &qeth_l2_netdev_ops;
+       card->dev->priv_flags |= IFF_UNICAST_FLT;
+
        if (IS_OSM(card)) {
                card->dev->features |= NETIF_F_VLAN_CHALLENGED;
        } else {
@@ -786,6 +771,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card, bool carrier_ok)
                                       PAGE_SIZE * (QDIO_MAX_ELEMENTS_PER_BUFFER - 1));
        }
 
+add_napi:
        netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT);
        rc = register_netdev(card->dev);
        if (!rc && carrier_ok)
@@ -1120,20 +1106,14 @@ static int qeth_osn_send_control_data(struct qeth_card *card, int len,
 }
 
 static int qeth_osn_send_ipa_cmd(struct qeth_card *card,
-                       struct qeth_cmd_buffer *iob, int data_len)
+                                struct qeth_cmd_buffer *iob)
 {
-       u16 s1, s2;
+       u16 length;
 
        QETH_CARD_TEXT(card, 4, "osndipa");
 
-       qeth_prepare_ipa_cmd(card, iob);
-       s1 = (u16)(IPA_PDU_HEADER_SIZE + data_len);
-       s2 = (u16)data_len;
-       memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2);
-       memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2);
-       memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2);
-       memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2);
-       return qeth_osn_send_control_data(card, s1, iob);
+       memcpy(&length, QETH_IPA_PDU_LEN_TOTAL(iob->data), 2);
+       return qeth_osn_send_control_data(card, length, iob);
 }
 
 int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
@@ -1150,8 +1130,9 @@ int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
        if (!qeth_card_hw_is_reachable(card))
                return -ENODEV;
        iob = qeth_wait_for_buffer(&card->write);
+       qeth_prepare_ipa_cmd(card, iob, (u16) data_len);
        memcpy(__ipa_cmd(iob), data, data_len);
-       return qeth_osn_send_ipa_cmd(card, iob, data_len);
+       return qeth_osn_send_ipa_cmd(card, iob);
 }
 EXPORT_SYMBOL(qeth_osn_assist);
 
@@ -1330,7 +1311,7 @@ static void qeth_bridge_state_change(struct qeth_card *card,
        data->card = card;
        memcpy(&data->qports, qports,
                        sizeof(struct qeth_sbp_state_change) + extrasize);
-       queue_work(qeth_wq, &data->worker);
+       queue_work(card->event_wq, &data->worker);
 }
 
 struct qeth_bridge_host_data {
@@ -1402,14 +1383,12 @@ static void qeth_bridge_host_event(struct qeth_card *card,
        data->card = card;
        memcpy(&data->hostevs, hostevs,
                        sizeof(struct qeth_ipacmd_addr_change) + extrasize);
-       queue_work(qeth_wq, &data->worker);
+       queue_work(card->event_wq, &data->worker);
 }
 
 /* SETBRIDGEPORT support; sending commands */
 
 struct _qeth_sbp_cbctl {
-       u16 ipa_rc;
-       u16 cmd_rc;
        union {
                u32 supported;
                struct {
@@ -1419,23 +1398,21 @@ struct _qeth_sbp_cbctl {
        } data;
 };
 
-/**
- * qeth_bridgeport_makerc() - derive "traditional" error from hardware codes.
- * @card:                    qeth_card structure pointer, for debug messages.
- * @cbctl:                   state structure with hardware return codes.
- * @setcmd:                  IPA command code
- *
- * Returns negative errno-compatible error indication or 0 on success.
- */
 static int qeth_bridgeport_makerc(struct qeth_card *card,
-       struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd)
+                                 struct qeth_ipa_cmd *cmd)
 {
+       struct qeth_ipacmd_setbridgeport *sbp = &cmd->data.sbp;
+       enum qeth_ipa_sbp_cmd setcmd = sbp->hdr.command_code;
+       u16 ipa_rc = cmd->hdr.return_code;
+       u16 sbp_rc = sbp->hdr.return_code;
        int rc;
-       int is_iqd = (card->info.type == QETH_CARD_TYPE_IQD);
 
-       if ((is_iqd && (cbctl->ipa_rc == IPA_RC_SUCCESS)) ||
-           (!is_iqd && (cbctl->ipa_rc == cbctl->cmd_rc)))
-               switch (cbctl->cmd_rc) {
+       if (ipa_rc == IPA_RC_SUCCESS && sbp_rc == IPA_RC_SUCCESS)
+               return 0;
+
+       if ((IS_IQD(card) && ipa_rc == IPA_RC_SUCCESS) ||
+           (!IS_IQD(card) && ipa_rc == sbp_rc)) {
+               switch (sbp_rc) {
                case IPA_RC_SUCCESS:
                        rc = 0;
                        break;
@@ -1499,8 +1476,8 @@ static int qeth_bridgeport_makerc(struct qeth_card *card,
                default:
                        rc = -EIO;
                }
-       else
-               switch (cbctl->ipa_rc) {
+       } else {
+               switch (ipa_rc) {
                case IPA_RC_NOTSUPP:
                        rc = -EOPNOTSUPP;
                        break;
@@ -1510,10 +1487,11 @@ static int qeth_bridgeport_makerc(struct qeth_card *card,
                default:
                        rc = -EIO;
                }
+       }
 
        if (rc) {
-               QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc);
-               QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc);
+               QETH_CARD_TEXT_(card, 2, "SBPi%04x", ipa_rc);
+               QETH_CARD_TEXT_(card, 2, "SBPc%04x", sbp_rc);
        }
        return rc;
 }
@@ -1545,15 +1523,15 @@ static int qeth_bridgeport_query_support_cb(struct qeth_card *card,
 {
        struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
        struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
+       int rc;
+
        QETH_CARD_TEXT(card, 2, "brqsupcb");
-       cbctl->ipa_rc = cmd->hdr.return_code;
-       cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
-       if ((cbctl->ipa_rc == 0) && (cbctl->cmd_rc == 0)) {
-               cbctl->data.supported =
-                       cmd->data.sbp.data.query_cmds_supp.supported_cmds;
-       } else {
-               cbctl->data.supported = 0;
-       }
+       rc = qeth_bridgeport_makerc(card, cmd);
+       if (rc)
+               return rc;
+
+       cbctl->data.supported =
+               cmd->data.sbp.data.query_cmds_supp.supported_cmds;
        return 0;
 }
 
@@ -1574,12 +1552,11 @@ static void qeth_bridgeport_query_support(struct qeth_card *card)
                                 sizeof(struct qeth_sbp_query_cmds_supp));
        if (!iob)
                return;
+
        if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb,
-                                                       (void *)&cbctl) ||
-           qeth_bridgeport_makerc(card, &cbctl,
-                                       IPA_SBP_QUERY_COMMANDS_SUPPORTED)) {
-               /* non-zero makerc signifies failure, and produce messages */
+                             &cbctl)) {
                card->options.sbp.role = QETH_SBP_ROLE_NONE;
+               card->options.sbp.supported_funcs = 0;
                return;
        }
        card->options.sbp.supported_funcs = cbctl.data.supported;
@@ -1591,16 +1568,16 @@ static int qeth_bridgeport_query_ports_cb(struct qeth_card *card,
        struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
        struct qeth_sbp_query_ports *qports = &cmd->data.sbp.data.query_ports;
        struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
+       int rc;
 
        QETH_CARD_TEXT(card, 2, "brqprtcb");
-       cbctl->ipa_rc = cmd->hdr.return_code;
-       cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
-       if ((cbctl->ipa_rc != 0) || (cbctl->cmd_rc != 0))
-               return 0;
+       rc = qeth_bridgeport_makerc(card, cmd);
+       if (rc)
+               return rc;
+
        if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) {
-               cbctl->cmd_rc = 0xffff;
                QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length);
-               return 0;
+               return -EINVAL;
        }
        /* first entry contains the state of the local port */
        if (qports->num_entries > 0) {
@@ -1625,7 +1602,6 @@ static int qeth_bridgeport_query_ports_cb(struct qeth_card *card,
 int qeth_bridgeport_query_ports(struct qeth_card *card,
        enum qeth_sbp_roles *role, enum qeth_sbp_states *state)
 {
-       int rc = 0;
        struct qeth_cmd_buffer *iob;
        struct _qeth_sbp_cbctl cbctl = {
                .data = {
@@ -1642,22 +1618,18 @@ int qeth_bridgeport_query_ports(struct qeth_card *card,
        iob = qeth_sbp_build_cmd(card, IPA_SBP_QUERY_BRIDGE_PORTS, 0);
        if (!iob)
                return -ENOMEM;
-       rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb,
-                               (void *)&cbctl);
-       if (rc < 0)
-               return rc;
-       return qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS);
+
+       return qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb,
+                                &cbctl);
 }
 
 static int qeth_bridgeport_set_cb(struct qeth_card *card,
        struct qeth_reply *reply, unsigned long data)
 {
        struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data;
-       struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
+
        QETH_CARD_TEXT(card, 2, "brsetrcb");
-       cbctl->ipa_rc = cmd->hdr.return_code;
-       cbctl->cmd_rc = cmd->data.sbp.hdr.return_code;
-       return 0;
+       return qeth_bridgeport_makerc(card, cmd);
 }
 
 /**
@@ -1669,10 +1641,8 @@ static int qeth_bridgeport_set_cb(struct qeth_card *card,
  */
 int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
 {
-       int rc = 0;
        int cmdlength;
        struct qeth_cmd_buffer *iob;
-       struct _qeth_sbp_cbctl cbctl;
        enum qeth_ipa_sbp_cmd setcmd;
 
        QETH_CARD_TEXT(card, 2, "brsetrol");
@@ -1697,11 +1667,8 @@ int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
        iob = qeth_sbp_build_cmd(card, setcmd, cmdlength);
        if (!iob)
                return -ENOMEM;
-       rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb,
-                               (void *)&cbctl);
-       if (rc < 0)
-               return rc;
-       return qeth_bridgeport_makerc(card, &cbctl, setcmd);
+
+       return qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, NULL);
 }
 
 /**
@@ -1805,7 +1772,7 @@ static bool qeth_bridgeport_is_in_use(struct qeth_card *card)
 /* VNIC Characteristics support */
 
 /* handle VNICC IPA command return codes; convert to error codes */
-static int qeth_l2_vnicc_makerc(struct qeth_card *card, int ipa_rc)
+static int qeth_l2_vnicc_makerc(struct qeth_card *card, u16 ipa_rc)
 {
        int rc;
 
@@ -1863,7 +1830,7 @@ static int qeth_l2_vnicc_request_cb(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 2, "vniccrcb");
        if (cmd->hdr.return_code)
-               return 0;
+               return qeth_l2_vnicc_makerc(card, cmd->hdr.return_code);
        /* return results to caller */
        card->options.vnicc.sup_chars = rep->hdr.sup;
        card->options.vnicc.cur_chars = rep->hdr.cur;
@@ -1884,7 +1851,6 @@ static int qeth_l2_vnicc_request(struct qeth_card *card,
        struct qeth_ipacmd_vnicc *req;
        struct qeth_cmd_buffer *iob;
        struct qeth_ipa_cmd *cmd;
-       int rc;
 
        QETH_CARD_TEXT(card, 2, "vniccreq");
 
@@ -1927,10 +1893,7 @@ static int qeth_l2_vnicc_request(struct qeth_card *card,
        }
 
        /* send request */
-       rc = qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb,
-                              (void *) cbctl);
-
-       return qeth_l2_vnicc_makerc(card, rc);
+       return qeth_send_ipa_cmd(card, iob, qeth_l2_vnicc_request_cb, cbctl);
 }
 
 /* VNICC query VNIC characteristics request */
index 59535ec..07c3149 100644 (file)
@@ -253,8 +253,7 @@ static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
                } else
                        rc = qeth_l3_register_addr_entry(card, addr);
 
-               if (!rc || (rc == IPA_RC_DUPLICATE_IP_ADDRESS) ||
-                               (rc == IPA_RC_LAN_OFFLINE)) {
+               if (!rc || rc == -EADDRINUSE || rc == -ENETDOWN) {
                        addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
                        if (addr->ref_counter < 1) {
                                qeth_l3_deregister_addr_entry(card, addr);
@@ -338,10 +337,28 @@ static void qeth_l3_recover_ip(struct qeth_card *card)
 
 }
 
+static int qeth_l3_setdelip_cb(struct qeth_card *card, struct qeth_reply *reply,
+                              unsigned long data)
+{
+       struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
+
+       switch (cmd->hdr.return_code) {
+       case IPA_RC_SUCCESS:
+               return 0;
+       case IPA_RC_DUPLICATE_IP_ADDRESS:
+               return -EADDRINUSE;
+       case IPA_RC_MC_ADDR_NOT_FOUND:
+               return -ENOENT;
+       case IPA_RC_LAN_OFFLINE:
+               return -ENETDOWN;
+       default:
+               return -EIO;
+       }
+}
+
 static int qeth_l3_send_setdelmc(struct qeth_card *card,
                        struct qeth_ipaddr *addr, int ipacmd)
 {
-       int rc;
        struct qeth_cmd_buffer *iob;
        struct qeth_ipa_cmd *cmd;
 
@@ -358,9 +375,7 @@ static int qeth_l3_send_setdelmc(struct qeth_card *card,
        else
                memcpy(&cmd->data.setdelipm.ip4, &addr->u.a4.addr, 4);
 
-       rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
-
-       return rc;
+       return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL);
 }
 
 static void qeth_l3_fill_netmask(u8 *netmask, unsigned int len)
@@ -422,7 +437,7 @@ static int qeth_l3_send_setdelip(struct qeth_card *card,
                cmd->data.setdelip4.flags = flags;
        }
 
-       return qeth_send_ipa_cmd(card, iob, NULL, NULL);
+       return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL);
 }
 
 static int qeth_l3_send_setrouting(struct qeth_card *card,
@@ -942,12 +957,13 @@ static int qeth_l3_start_ipassists(struct qeth_card *card)
 static int qeth_l3_iqd_read_initial_mac_cb(struct qeth_card *card,
                struct qeth_reply *reply, unsigned long data)
 {
-       struct qeth_ipa_cmd *cmd;
+       struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
 
-       cmd = (struct qeth_ipa_cmd *) data;
-       if (cmd->hdr.return_code == 0)
-               ether_addr_copy(card->dev->dev_addr,
-                               cmd->data.create_destroy_addr.unique_id);
+       if (cmd->hdr.return_code)
+               return -EIO;
+
+       ether_addr_copy(card->dev->dev_addr,
+                       cmd->data.create_destroy_addr.unique_id);
        return 0;
 }
 
@@ -975,19 +991,18 @@ static int qeth_l3_iqd_read_initial_mac(struct qeth_card *card)
 static int qeth_l3_get_unique_id_cb(struct qeth_card *card,
                struct qeth_reply *reply, unsigned long data)
 {
-       struct qeth_ipa_cmd *cmd;
+       struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
 
-       cmd = (struct qeth_ipa_cmd *) data;
-       if (cmd->hdr.return_code == 0)
+       if (cmd->hdr.return_code == 0) {
                card->info.unique_id = *((__u16 *)
                                &cmd->data.create_destroy_addr.unique_id[6]);
-       else {
-               card->info.unique_id =  UNIQUE_ID_IF_CREATE_ADDR_FAILED |
-                                       UNIQUE_ID_NOT_BY_CARD;
-               dev_warn(&card->gdev->dev, "The network adapter failed to "
-                       "generate a unique ID\n");
+               return 0;
        }
-       return 0;
+
+       card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
+                              UNIQUE_ID_NOT_BY_CARD;
+       dev_warn(&card->gdev->dev, "The network adapter failed to generate a unique ID\n");
+       return -EIO;
 }
 
 static int qeth_l3_get_unique_id(struct qeth_card *card)
@@ -1070,7 +1085,7 @@ qeth_diags_trace_cb(struct qeth_card *card, struct qeth_reply *reply,
                                 cmd->data.diagass.action, CARD_DEVID(card));
        }
 
-       return 0;
+       return rc ? -EIO : 0;
 }
 
 static int
@@ -1300,12 +1315,11 @@ static void qeth_l3_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
                                ip_eth_mc_map(ip_hdr(skb)->daddr, tg_addr);
                        else
                                ipv6_eth_mc_map(&ipv6_hdr(skb)->daddr, tg_addr);
-
-                       card->stats.multicast++;
+                       QETH_CARD_STAT_INC(card, rx_multicast);
                        break;
                case QETH_CAST_BROADCAST:
                        ether_addr_copy(tg_addr, card->dev->broadcast);
-                       card->stats.multicast++;
+                       QETH_CARD_STAT_INC(card, rx_multicast);
                        break;
                default:
                        if (card->options.sniffer)
@@ -1386,8 +1400,8 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
                }
                work_done++;
                budget--;
-               card->stats.rx_packets++;
-               card->stats.rx_bytes += len;
+               QETH_CARD_STAT_INC(card, rx_packets);
+               QETH_CARD_STAT_ADD(card, rx_bytes, len);
        }
        return work_done;
 }
@@ -1428,6 +1442,8 @@ static void qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
                qeth_clear_cmd_buffers(&card->read);
                qeth_clear_cmd_buffers(&card->write);
        }
+
+       flush_workqueue(card->event_wq);
 }
 
 /*
@@ -1479,14 +1495,14 @@ static void qeth_l3_set_rx_mode(struct net_device *dev)
                        switch (addr->disp_flag) {
                        case QETH_DISP_ADDR_DELETE:
                                rc = qeth_l3_deregister_addr_entry(card, addr);
-                               if (!rc || rc == IPA_RC_MC_ADDR_NOT_FOUND) {
+                               if (!rc || rc == -ENOENT) {
                                        hash_del(&addr->hnode);
                                        kfree(addr);
                                }
                                break;
                        case QETH_DISP_ADDR_ADD:
                                rc = qeth_l3_register_addr_entry(card, addr);
-                               if (rc && rc != IPA_RC_LAN_OFFLINE) {
+                               if (rc && rc != -ENETDOWN) {
                                        hash_del(&addr->hnode);
                                        kfree(addr);
                                        break;
@@ -1507,7 +1523,7 @@ static void qeth_l3_set_rx_mode(struct net_device *dev)
        qeth_l3_handle_promisc_mode(card);
 }
 
-static int qeth_l3_arp_makerc(int rc)
+static int qeth_l3_arp_makerc(u16 rc)
 {
        switch (rc) {
        case IPA_RC_SUCCESS:
@@ -1524,8 +1540,18 @@ static int qeth_l3_arp_makerc(int rc)
        }
 }
 
+static int qeth_l3_arp_cmd_cb(struct qeth_card *card, struct qeth_reply *reply,
+                             unsigned long data)
+{
+       struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
+
+       qeth_setassparms_cb(card, reply, data);
+       return qeth_l3_arp_makerc(cmd->hdr.return_code);
+}
+
 static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
 {
+       struct qeth_cmd_buffer *iob;
        int rc;
 
        QETH_CARD_TEXT(card, 3, "arpstnoe");
@@ -1540,13 +1566,19 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
        if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
                return -EOPNOTSUPP;
        }
-       rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
-                                         IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
-                                         no_entries);
+
+       iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+                                      IPA_CMD_ASS_ARP_SET_NO_ENTRIES, 4,
+                                      QETH_PROT_IPV4);
+       if (!iob)
+               return -ENOMEM;
+
+       __ipa_cmd(iob)->data.setassparms.data.flags_32bit = (u32) no_entries;
+       rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL);
        if (rc)
                QETH_DBF_MESSAGE(2, "Could not set number of ARP entries on device %x: %#x\n",
                                 CARD_DEVID(card), rc);
-       return qeth_l3_arp_makerc(rc);
+       return rc;
 }
 
 static __u32 get_arp_entry_size(struct qeth_card *card,
@@ -1597,7 +1629,6 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card,
        struct qeth_ipa_cmd *cmd;
        struct qeth_arp_query_data *qdata;
        struct qeth_arp_query_info *qinfo;
-       int i;
        int e;
        int entrybytes_done;
        int stripped_bytes;
@@ -1611,13 +1642,13 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card,
        if (cmd->hdr.return_code) {
                QETH_CARD_TEXT(card, 4, "arpcberr");
                QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
-               return 0;
+               return qeth_l3_arp_makerc(cmd->hdr.return_code);
        }
        if (cmd->data.setassparms.hdr.return_code) {
                cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
                QETH_CARD_TEXT(card, 4, "setaperr");
                QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
-               return 0;
+               return qeth_l3_arp_makerc(cmd->hdr.return_code);
        }
        qdata = &cmd->data.setassparms.data.query_arp;
        QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries);
@@ -1644,9 +1675,9 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card,
                        break;
 
                if ((qinfo->udata_len - qinfo->udata_offset) < esize) {
-                       QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM);
-                       cmd->hdr.return_code = IPA_RC_ENOMEM;
-                       goto out_error;
+                       QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOSPC);
+                       memset(qinfo->udata, 0, 4);
+                       return -ENOSPC;
                }
 
                memcpy(qinfo->udata + qinfo->udata_offset,
@@ -1669,10 +1700,6 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card,
        memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2);
        QETH_CARD_TEXT_(card, 4, "rc%i", 0);
        return 0;
-out_error:
-       i = 0;
-       memcpy(qinfo->udata, &i, 4);
-       return 0;
 }
 
 static int qeth_l3_query_arp_cache_info(struct qeth_card *card,
@@ -1694,13 +1721,11 @@ static int qeth_l3_query_arp_cache_info(struct qeth_card *card,
                return -ENOMEM;
        cmd = __ipa_cmd(iob);
        cmd->data.setassparms.data.query_arp.request_bits = 0x000F;
-       rc = qeth_send_control_data(card,
-                                   QETH_SETASS_BASE_LEN + QETH_ARP_CMD_LEN,
-                                   iob, qeth_l3_arp_query_cb, qinfo);
+       rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_query_cb, qinfo);
        if (rc)
                QETH_DBF_MESSAGE(2, "Error while querying ARP cache on device %x: %#x\n",
                                 CARD_DEVID(card), rc);
-       return qeth_l3_arp_makerc(rc);
+       return rc;
 }
 
 static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata)
@@ -1782,16 +1807,16 @@ static int qeth_l3_arp_modify_entry(struct qeth_card *card,
        cmd_entry = &__ipa_cmd(iob)->data.setassparms.data.arp_entry;
        ether_addr_copy(cmd_entry->macaddr, entry->macaddr);
        memcpy(cmd_entry->ipaddr, entry->ipaddr, 4);
-       rc = qeth_send_ipa_cmd(card, iob, qeth_setassparms_cb, NULL);
+       rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL);
        if (rc)
                QETH_DBF_MESSAGE(2, "Could not modify (cmd: %#x) ARP entry on device %x: %#x\n",
                                 arp_cmd, CARD_DEVID(card), rc);
-
-       return qeth_l3_arp_makerc(rc);
+       return rc;
 }
 
 static int qeth_l3_arp_flush_cache(struct qeth_card *card)
 {
+       struct qeth_cmd_buffer *iob;
        int rc;
 
        QETH_CARD_TEXT(card, 3, "arpflush");
@@ -1806,12 +1831,18 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card)
        if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
                return -EOPNOTSUPP;
        }
-       rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
-                                         IPA_CMD_ASS_ARP_FLUSH_CACHE, 0);
+
+       iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+                                      IPA_CMD_ASS_ARP_FLUSH_CACHE, 0,
+                                      QETH_PROT_IPV4);
+       if (!iob)
+               return -ENOMEM;
+
+       rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL);
        if (rc)
                QETH_DBF_MESSAGE(2, "Could not flush ARP cache on device %x: %#x\n",
                                 CARD_DEVID(card), rc);
-       return qeth_l3_arp_makerc(rc);
+       return rc;
 }
 
 static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
@@ -1913,12 +1944,13 @@ static u8 qeth_l3_cast_type_to_flag(int cast_type)
        return QETH_CAST_UNICAST;
 }
 
-static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
-                               struct sk_buff *skb, int ipv, int cast_type,
-                               unsigned int data_len)
+static void qeth_l3_fill_header(struct qeth_qdio_out_q *queue,
+                               struct qeth_hdr *hdr, struct sk_buff *skb,
+                               int ipv, int cast_type, unsigned int data_len)
 {
        struct qeth_hdr_layer3 *l3_hdr = &hdr->hdr.l3;
        struct vlan_ethhdr *veth = vlan_eth_hdr(skb);
+       struct qeth_card *card = queue->card;
 
        hdr->hdr.l3.length = data_len;
 
@@ -1940,8 +1972,7 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                        /* some HW requires combined L3+L4 csum offload: */
                        if (ipv == 4)
                                hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_HDR_REQ;
-                       if (card->options.performance_stats)
-                               card->perf_stats.tx_csum++;
+                       QETH_TXQ_STAT_INC(queue, skbs_csum);
                }
        }
 
@@ -2042,6 +2073,8 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
        int tx_bytes = skb->len;
        int rc;
 
+       queue = qeth_get_tx_queue(card, skb, ipv, cast_type);
+
        if (IS_IQD(card)) {
                if (card->options.sniffer)
                        goto tx_drop;
@@ -2052,19 +2085,13 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
        }
 
        if (card->state != CARD_STATE_UP) {
-               card->stats.tx_carrier_errors++;
+               QETH_TXQ_STAT_INC(queue, tx_carrier_errors);
                goto tx_drop;
        }
 
        if (cast_type == RTN_BROADCAST && !card->info.broadcast_capable)
                goto tx_drop;
 
-       queue = qeth_get_tx_queue(card, skb, ipv, cast_type);
-
-       if (card->options.performance_stats) {
-               card->perf_stats.outbound_cnt++;
-               card->perf_stats.outbound_start_time = qeth_get_micros();
-       }
        netif_stop_queue(dev);
 
        if (ipv == 4 || IS_IQD(card))
@@ -2074,11 +2101,8 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
                               qeth_l3_fill_header);
 
        if (!rc) {
-               card->stats.tx_packets++;
-               card->stats.tx_bytes += tx_bytes;
-               if (card->options.performance_stats)
-                       card->perf_stats.outbound_time += qeth_get_micros() -
-                               card->perf_stats.outbound_start_time;
+               QETH_TXQ_STAT_INC(queue, tx_packets);
+               QETH_TXQ_STAT_ADD(queue, tx_bytes, tx_bytes);
                netif_wake_queue(dev);
                return NETDEV_TX_OK;
        } else if (rc == -EBUSY) {
@@ -2086,22 +2110,13 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
        } /* else fall through */
 
 tx_drop:
-       card->stats.tx_dropped++;
-       card->stats.tx_errors++;
+       QETH_TXQ_STAT_INC(queue, tx_dropped);
+       QETH_TXQ_STAT_INC(queue, tx_errors);
        dev_kfree_skb_any(skb);
        netif_wake_queue(dev);
        return NETDEV_TX_OK;
 }
 
-static const struct ethtool_ops qeth_l3_ethtool_ops = {
-       .get_link = ethtool_op_get_link,
-       .get_strings = qeth_core_get_strings,
-       .get_ethtool_stats = qeth_core_get_ethtool_stats,
-       .get_sset_count = qeth_core_get_sset_count,
-       .get_drvinfo = qeth_core_get_drvinfo,
-       .get_link_ksettings = qeth_core_ethtool_get_link_ksettings,
-};
-
 /*
  * we need NOARP for IPv4 but we want neighbor solicitation for IPv6. Setting
  * NOARP on the netdevice is no option because it also turns off neighbor
@@ -2138,7 +2153,7 @@ static netdev_features_t qeth_l3_osa_features_check(struct sk_buff *skb,
 static const struct net_device_ops qeth_l3_netdev_ops = {
        .ndo_open               = qeth_open,
        .ndo_stop               = qeth_stop,
-       .ndo_get_stats          = qeth_get_stats,
+       .ndo_get_stats64        = qeth_get_stats64,
        .ndo_start_xmit         = qeth_l3_hard_start_xmit,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_set_rx_mode        = qeth_l3_set_rx_mode,
@@ -2153,7 +2168,7 @@ static const struct net_device_ops qeth_l3_netdev_ops = {
 static const struct net_device_ops qeth_l3_osa_netdev_ops = {
        .ndo_open               = qeth_open,
        .ndo_stop               = qeth_stop,
-       .ndo_get_stats          = qeth_get_stats,
+       .ndo_get_stats64        = qeth_get_stats64,
        .ndo_start_xmit         = qeth_l3_hard_start_xmit,
        .ndo_features_check     = qeth_l3_osa_features_check,
        .ndo_validate_addr      = eth_validate_addr,
@@ -2223,7 +2238,6 @@ static int qeth_l3_setup_netdev(struct qeth_card *card, bool carrier_ok)
                return -ENODEV;
 
        card->dev->needed_headroom = headroom;
-       card->dev->ethtool_ops = &qeth_l3_ethtool_ops;
        card->dev->features |=  NETIF_F_HW_VLAN_CTAG_TX |
                                NETIF_F_HW_VLAN_CTAG_RX |
                                NETIF_F_HW_VLAN_CTAG_FILTER;
@@ -2278,6 +2292,7 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev)
        if (cgdev->state == CCWGROUP_ONLINE)
                qeth_l3_set_offline(cgdev);
 
+       cancel_work_sync(&card->close_dev_work);
        if (qeth_netdev_is_registered(card->dev))
                unregister_netdev(card->dev);
        qeth_l3_clear_ip_htable(card, 0);
index 9cf30d1..e390f8c 100644 (file)
@@ -403,7 +403,6 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device)
                goto failed;
 
        /* report size limit per scatter-gather segment */
-       adapter->dma_parms.max_segment_size = ZFCP_QDIO_SBALE_LEN;
        adapter->ccw_device->dev.dma_parms = &adapter->dma_parms;
 
        adapter->stat_read_buf_num = FSF_STATUS_READS_RECOM;
index 00acc71..f4f6a07 100644 (file)
@@ -428,6 +428,8 @@ static struct scsi_host_template zfcp_scsi_host_template = {
        .max_sectors             = (((QDIO_MAX_ELEMENTS_PER_BUFFER - 1)
                                     * ZFCP_QDIO_MAX_SBALS_PER_REQ) - 2) * 8,
                                   /* GCD, adjusted later */
+       /* report size limit per scatter-gather segment */
+       .max_segment_size        = ZFCP_QDIO_SBALE_LEN,
        .dma_boundary            = ZFCP_QDIO_SBALE_LEN - 1,
        .shost_attrs             = zfcp_sysfs_shost_attrs,
        .sdev_attrs              = zfcp_sysfs_sdev_attrs,
index 128d658..16957d7 100644 (file)
@@ -295,7 +295,7 @@ NCR_700_detect(struct scsi_host_template *tpnt,
        if(tpnt->sdev_attrs == NULL)
                tpnt->sdev_attrs = NCR_700_dev_attrs;
 
-       memory = dma_alloc_attrs(hostdata->dev, TOTAL_MEM_SIZE, &pScript,
+       memory = dma_alloc_attrs(dev, TOTAL_MEM_SIZE, &pScript,
                                 GFP_KERNEL, DMA_ATTR_NON_CONSISTENT);
        if(memory == NULL) {
                printk(KERN_ERR "53c700: Failed to allocate memory for driver, detaching\n");
index f83f79b..07efcb9 100644 (file)
@@ -280,7 +280,7 @@ static ssize_t asd_show_dev_rev(struct device *dev,
        return snprintf(buf, PAGE_SIZE, "%s\n",
                        asd_dev_rev[asd_ha->revision_id]);
 }
-static DEVICE_ATTR(revision, S_IRUGO, asd_show_dev_rev, NULL);
+static DEVICE_ATTR(aic_revision, S_IRUGO, asd_show_dev_rev, NULL);
 
 static ssize_t asd_show_dev_bios_build(struct device *dev,
                                       struct device_attribute *attr,char *buf)
@@ -477,7 +477,7 @@ static int asd_create_dev_attrs(struct asd_ha_struct *asd_ha)
 {
        int err;
 
-       err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+       err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_aic_revision);
        if (err)
                return err;
 
@@ -499,13 +499,13 @@ err_update_bios:
 err_biosb:
        device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
 err_rev:
-       device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+       device_remove_file(&asd_ha->pcidev->dev, &dev_attr_aic_revision);
        return err;
 }
 
 static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha)
 {
-       device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+       device_remove_file(&asd_ha->pcidev->dev, &dev_attr_aic_revision);
        device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
        device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
        device_remove_file(&asd_ha->pcidev->dev, &dev_attr_update_bios);
index 350257c..bc9f2a2 100644 (file)
@@ -240,6 +240,7 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba)
                return NULL;
        }
 
+       cmgr->hba = hba;
        cmgr->free_list = kcalloc(arr_sz, sizeof(*cmgr->free_list),
                                  GFP_KERNEL);
        if (!cmgr->free_list) {
@@ -256,7 +257,6 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba)
                goto mem_err;
        }
 
-       cmgr->hba = hba;
        cmgr->cmds = (struct bnx2fc_cmd **)(cmgr + 1);
 
        for (i = 0; i < arr_sz; i++)  {
@@ -295,7 +295,7 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba)
 
        /* Allocate pool of io_bdts - one for each bnx2fc_cmd */
        mem_size = num_ios * sizeof(struct io_bdt *);
-       cmgr->io_bdt_pool = kmalloc(mem_size, GFP_KERNEL);
+       cmgr->io_bdt_pool = kzalloc(mem_size, GFP_KERNEL);
        if (!cmgr->io_bdt_pool) {
                printk(KERN_ERR PFX "failed to alloc io_bdt_pool\n");
                goto mem_err;
index bfa13e3..c8bad2c 100644 (file)
@@ -3687,6 +3687,7 @@ static int cxlflash_probe(struct pci_dev *pdev,
        host->max_cmd_len = CXLFLASH_MAX_CDB_LEN;
 
        cfg = shost_priv(host);
+       cfg->state = STATE_PROBING;
        cfg->host = host;
        rc = alloc_mem(cfg);
        if (rc) {
@@ -3775,6 +3776,7 @@ out:
        return rc;
 
 out_remove:
+       cfg->state = STATE_PROBED;
        cxlflash_remove(pdev);
        goto out;
 }
index be83590..ff943f4 100644 (file)
@@ -1726,14 +1726,14 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
            fc_frame_payload_op(fp) != ELS_LS_ACC) {
                FC_LPORT_DBG(lport, "FLOGI not accepted or bad response\n");
                fc_lport_error(lport, fp);
-               goto err;
+               goto out;
        }
 
        flp = fc_frame_payload_get(fp, sizeof(*flp));
        if (!flp) {
                FC_LPORT_DBG(lport, "FLOGI bad response\n");
                fc_lport_error(lport, fp);
-               goto err;
+               goto out;
        }
 
        mfs = ntohs(flp->fl_csp.sp_bb_data) &
@@ -1743,7 +1743,7 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
                FC_LPORT_DBG(lport, "FLOGI bad mfs:%hu response, "
                             "lport->mfs:%hu\n", mfs, lport->mfs);
                fc_lport_error(lport, fp);
-               goto err;
+               goto out;
        }
 
        if (mfs <= lport->mfs) {
index 9192a1d..dfba492 100644 (file)
@@ -184,7 +184,6 @@ void fc_rport_destroy(struct kref *kref)
        struct fc_rport_priv *rdata;
 
        rdata = container_of(kref, struct fc_rport_priv, kref);
-       WARN_ON(!list_empty(&rdata->peers));
        kfree_rcu(rdata, rcu);
 }
 EXPORT_SYMBOL(fc_rport_destroy);
index 661512b..e27f4df 100644 (file)
@@ -62,7 +62,7 @@
 
 /* make sure inq_product_rev string corresponds to this version */
 #define SDEBUG_VERSION "0188"  /* format to fit INQUIRY revision field */
-static const char *sdebug_version_date = "20180128";
+static const char *sdebug_version_date = "20190125";
 
 #define MY_NAME "scsi_debug"
 
@@ -735,7 +735,7 @@ static inline bool scsi_debug_lbp(void)
                (sdebug_lbpu || sdebug_lbpws || sdebug_lbpws10);
 }
 
-static void *fake_store(unsigned long long lba)
+static void *lba2fake_store(unsigned long long lba)
 {
        lba = do_div(lba, sdebug_store_sectors);
 
@@ -2514,8 +2514,8 @@ static int do_device_access(struct scsi_cmnd *scmd, u32 sg_skip, u64 lba,
        return ret;
 }
 
-/* If fake_store(lba,num) compares equal to arr(num), then copy top half of
- * arr into fake_store(lba,num) and return true. If comparison fails then
+/* If lba2fake_store(lba,num) compares equal to arr(num), then copy top half of
+ * arr into lba2fake_store(lba,num) and return true. If comparison fails then
  * return false. */
 static bool comp_write_worker(u64 lba, u32 num, const u8 *arr)
 {
@@ -2643,7 +2643,7 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec,
                if (sdt->app_tag == cpu_to_be16(0xffff))
                        continue;
 
-               ret = dif_verify(sdt, fake_store(sector), sector, ei_lba);
+               ret = dif_verify(sdt, lba2fake_store(sector), sector, ei_lba);
                if (ret) {
                        dif_errors++;
                        return ret;
@@ -3261,10 +3261,12 @@ err_out:
 static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
                           u32 ei_lba, bool unmap, bool ndob)
 {
+       int ret;
        unsigned long iflags;
        unsigned long long i;
-       int ret;
-       u64 lba_off;
+       u32 lb_size = sdebug_sector_size;
+       u64 block, lbaa;
+       u8 *fs1p;
 
        ret = check_device_access_params(scp, lba, num);
        if (ret)
@@ -3276,31 +3278,30 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
                unmap_region(lba, num);
                goto out;
        }
-
-       lba_off = lba * sdebug_sector_size;
+       lbaa = lba;
+       block = do_div(lbaa, sdebug_store_sectors);
        /* if ndob then zero 1 logical block, else fetch 1 logical block */
+       fs1p = fake_storep + (block * lb_size);
        if (ndob) {
-               memset(fake_storep + lba_off, 0, sdebug_sector_size);
+               memset(fs1p, 0, lb_size);
                ret = 0;
        } else
-               ret = fetch_to_dev_buffer(scp, fake_storep + lba_off,
-                                         sdebug_sector_size);
+               ret = fetch_to_dev_buffer(scp, fs1p, lb_size);
 
        if (-1 == ret) {
                write_unlock_irqrestore(&atomic_rw, iflags);
                return DID_ERROR << 16;
-       } else if (sdebug_verbose && !ndob && (ret < sdebug_sector_size))
+       } else if (sdebug_verbose && !ndob && (ret < lb_size))
                sdev_printk(KERN_INFO, scp->device,
                            "%s: %s: lb size=%u, IO sent=%d bytes\n",
-                           my_name, "write same",
-                           sdebug_sector_size, ret);
+                           my_name, "write same", lb_size, ret);
 
        /* Copy first sector to remaining blocks */
-       for (i = 1 ; i < num ; i++)
-               memcpy(fake_storep + ((lba + i) * sdebug_sector_size),
-                      fake_storep + lba_off,
-                      sdebug_sector_size);
-
+       for (i = 1 ; i < num ; i++) {
+               lbaa = lba + i;
+               block = do_div(lbaa, sdebug_store_sectors);
+               memmove(fake_storep + (block * lb_size), fs1p, lb_size);
+       }
        if (scsi_debug_lbp())
                map_region(lba, num);
 out:
index 83365b2..fff8694 100644 (file)
@@ -462,12 +462,16 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)
        sdkp->device->use_10_for_rw = 0;
 
        /*
-        * If something changed, revalidate the disk zone bitmaps once we have
-        * the capacity, that is on the second revalidate execution during disk
-        * scan and always during normal revalidate.
+        * Revalidate the disk zone bitmaps once the block device capacity is
+        * set on the second revalidate execution during disk scan and if
+        * something changed when executing a normal revalidate.
         */
-       if (sdkp->first_scan)
+       if (sdkp->first_scan) {
+               sdkp->zone_blocks = zone_blocks;
+               sdkp->nr_zones = nr_zones;
                return 0;
+       }
+
        if (sdkp->zone_blocks != zone_blocks ||
            sdkp->nr_zones != nr_zones ||
            disk->queue->nr_zones != nr_zones) {
index 52c153c..636f83f 100644 (file)
@@ -1143,18 +1143,19 @@ static void qm_mr_process_task(struct work_struct *work);
 static irqreturn_t portal_isr(int irq, void *ptr)
 {
        struct qman_portal *p = ptr;
-
-       u32 clear = QM_DQAVAIL_MASK | p->irq_sources;
        u32 is = qm_in(&p->p, QM_REG_ISR) & p->irq_sources;
+       u32 clear = 0;
 
        if (unlikely(!is))
                return IRQ_NONE;
 
        /* DQRR-handling if it's interrupt-driven */
-       if (is & QM_PIRQ_DQRI)
+       if (is & QM_PIRQ_DQRI) {
                __poll_portal_fast(p, QMAN_POLL_LIMIT);
+               clear = QM_DQAVAIL_MASK | QM_PIRQ_DQRI;
+       }
        /* Handling of anything else that's interrupt-driven */
-       clear |= __poll_portal_slow(p, is);
+       clear |= __poll_portal_slow(p, is) & QM_PIRQ_SLOW;
        qm_out(&p->p, QM_REG_ISR, clear);
        return IRQ_HANDLED;
 }
index daabace..1b3943b 100644 (file)
@@ -505,6 +505,17 @@ static netdev_tx_t port_dropframe(struct sk_buff *skb,
        return NETDEV_TX_OK;
 }
 
+static int swdev_get_port_parent_id(struct net_device *dev,
+                                   struct netdev_phys_item_id *ppid)
+{
+       struct ethsw_port_priv *port_priv = netdev_priv(dev);
+
+       ppid->id_len = 1;
+       ppid->id[0] = port_priv->ethsw_data->dev_id;
+
+       return 0;
+}
+
 static const struct net_device_ops ethsw_port_ops = {
        .ndo_open               = port_open,
        .ndo_stop               = port_stop,
@@ -515,6 +526,7 @@ static const struct net_device_ops ethsw_port_ops = {
        .ndo_get_offload_stats  = port_get_offload_stats,
 
        .ndo_start_xmit         = port_dropframe,
+       .ndo_get_port_parent_id = swdev_get_port_parent_id,
 };
 
 static void ethsw_links_state_update(struct ethsw_core *ethsw)
@@ -631,18 +643,7 @@ static void ethsw_teardown_irqs(struct fsl_mc_device *sw_dev)
 static int swdev_port_attr_get(struct net_device *netdev,
                               struct switchdev_attr *attr)
 {
-       struct ethsw_port_priv *port_priv = netdev_priv(netdev);
-
        switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = 1;
-               attr->u.ppid.id[0] = port_priv->ethsw_data->dev_id;
-               break;
-       case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
-               attr->u.brport_flags =
-                       (port_priv->ethsw_data->learning ? BR_LEARNING : 0) |
-                       (port_priv->flood ? BR_FLOOD : 0);
-               break;
        case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
                attr->u.brport_flags_support = BR_LEARNING | BR_FLOOD;
                break;
index 2848fa7..d6248ee 100644 (file)
@@ -170,7 +170,7 @@ int cvm_oct_phy_setup_device(struct net_device *dev)
                return -ENODEV;
 
        priv->last_link = 0;
-       phy_start_aneg(phydev);
+       phy_start(phydev);
 
        return 0;
 no_phy:
index c92bbd0..005de00 100644 (file)
@@ -265,7 +265,8 @@ static void spk_ttyio_send_xchar(char ch)
                return;
        }
 
-       speakup_tty->ops->send_xchar(speakup_tty, ch);
+       if (speakup_tty->ops->send_xchar)
+               speakup_tty->ops->send_xchar(speakup_tty, ch);
        mutex_unlock(&speakup_tty_mutex);
 }
 
@@ -277,7 +278,8 @@ static void spk_ttyio_tiocmset(unsigned int set, unsigned int clear)
                return;
        }
 
-       speakup_tty->ops->tiocmset(speakup_tty, set, clear);
+       if (speakup_tty->ops->tiocmset)
+               speakup_tty->ops->tiocmset(speakup_tty, set, clear);
        mutex_unlock(&speakup_tty_mutex);
 }
 
index 72016d0..8e7fffb 100644 (file)
@@ -852,6 +852,12 @@ static ssize_t pi_prot_type_store(struct config_item *item,
        return count;
 }
 
+/* always zero, but attr needs to remain RW to avoid userspace breakage */
+static ssize_t pi_prot_format_show(struct config_item *item, char *page)
+{
+       return snprintf(page, PAGE_SIZE, "0\n");
+}
+
 static ssize_t pi_prot_format_store(struct config_item *item,
                const char *page, size_t count)
 {
@@ -1132,7 +1138,7 @@ CONFIGFS_ATTR(, emulate_3pc);
 CONFIGFS_ATTR(, emulate_pr);
 CONFIGFS_ATTR(, pi_prot_type);
 CONFIGFS_ATTR_RO(, hw_pi_prot_type);
-CONFIGFS_ATTR_WO(, pi_prot_format);
+CONFIGFS_ATTR(, pi_prot_format);
 CONFIGFS_ATTR(, pi_prot_verify);
 CONFIGFS_ATTR(, enforce_pr_isids);
 CONFIGFS_ATTR(, is_nonrot);
index dfd2324..6fff161 100644 (file)
@@ -774,7 +774,7 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy)
 
                cdev = __cpufreq_cooling_register(np, policy, capacitance);
                if (IS_ERR(cdev)) {
-                       pr_err("cpu_cooling: cpu%d is not running as cooling device: %ld\n",
+                       pr_err("cpu_cooling: cpu%d failed to register as cooling device: %ld\n",
                               policy->cpu, PTR_ERR(cdev));
                        cdev = NULL;
                }
index 4bfdb4a..2df059c 100644 (file)
@@ -867,14 +867,14 @@ __init *thermal_of_build_thermal_zone(struct device_node *np)
 
        ret = of_property_read_u32(np, "polling-delay-passive", &prop);
        if (ret < 0) {
-               pr_err("missing polling-delay-passive property\n");
+               pr_err("%pOFn: missing polling-delay-passive property\n", np);
                goto free_tz;
        }
        tz->passive_delay = prop;
 
        ret = of_property_read_u32(np, "polling-delay", &prop);
        if (ret < 0) {
-               pr_err("missing polling-delay property\n");
+               pr_err("%pOFn: missing polling-delay property\n", np);
                goto free_tz;
        }
        tz->polling_delay = prop;
index e2c4076..c1fdbc0 100644 (file)
@@ -357,6 +357,9 @@ static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
        if (dmacnt == 2) {
                data->dma = devm_kzalloc(&pdev->dev, sizeof(*data->dma),
                                         GFP_KERNEL);
+               if (!data->dma)
+                       return -ENOMEM;
+
                data->dma->fn = mtk8250_dma_filter;
                data->dma->rx_size = MTK_UART_RX_SIZE;
                data->dma->rxconf.src_maxburst = MTK_UART_RX_TRIGGER;
index f80a300..48bd694 100644 (file)
@@ -3420,6 +3420,11 @@ static int
 serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
 {
        int num_iomem, num_port, first_port = -1, i;
+       int rc;
+
+       rc = serial_pci_is_class_communication(dev);
+       if (rc)
+               return rc;
 
        /*
         * Should we try to make guesses for multiport serial devices later?
@@ -3647,10 +3652,6 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
 
        board = &pci_boards[ent->driver_data];
 
-       rc = serial_pci_is_class_communication(dev);
-       if (rc)
-               return rc;
-
        rc = serial_pci_is_blacklisted(dev);
        if (rc)
                return rc;
index e1a551a..ce81523 100644 (file)
 #include <linux/serial_core.h>
 #include <asm/sbi.h>
 
-static void sbi_console_write(struct console *con,
-                             const char *s, unsigned int n)
+static void sbi_putc(struct uart_port *port, int c)
 {
-       int i;
+       sbi_console_putchar(c);
+}
 
-       for (i = 0; i < n; ++i)
-               sbi_console_putchar(s[i]);
+static void sbi_console_write(struct console *con,
+                             const char *s, unsigned n)
+{
+       struct earlycon_device *dev = con->data;
+       uart_console_write(&dev->port, s, n, sbi_putc);
 }
 
 static int __init early_sbi_setup(struct earlycon_device *device,
index 5c01bb6..556f50a 100644 (file)
@@ -130,6 +130,9 @@ static void uart_start(struct tty_struct *tty)
        struct uart_port *port;
        unsigned long flags;
 
+       if (!state)
+               return;
+
        port = uart_port_lock(state, flags);
        __uart_start(tty);
        uart_port_unlock(port, flags);
@@ -727,6 +730,9 @@ static void uart_unthrottle(struct tty_struct *tty)
        upstat_t mask = UPSTAT_SYNC_FIFO;
        struct uart_port *port;
 
+       if (!state)
+               return;
+
        port = uart_port_ref(state);
        if (!port)
                return;
index 8df0fd8..64bbeb7 100644 (file)
@@ -1921,7 +1921,7 @@ out_nomem:
 
 static void sci_free_irq(struct sci_port *port)
 {
-       int i;
+       int i, j;
 
        /*
         * Intentionally in reverse order so we iterate over the muxed
@@ -1937,6 +1937,13 @@ static void sci_free_irq(struct sci_port *port)
                if (unlikely(irq < 0))
                        continue;
 
+               /* Check if already freed (irq was muxed) */
+               for (j = 0; j < i; j++)
+                       if (port->irqs[j] == irq)
+                               j = i + 1;
+               if (j > i)
+                       continue;
+
                free_irq(port->irqs[i], port);
                kfree(port->irqstr[i]);
 
index cb7fcd7..c1e9ea6 100644 (file)
@@ -78,7 +78,7 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
        for (i = 0; i < exynos->num_clks; i++) {
                ret = clk_prepare_enable(exynos->clks[i]);
                if (ret) {
-                       while (--i > 0)
+                       while (i-- > 0)
                                clk_disable_unprepare(exynos->clks[i]);
                        return ret;
                }
@@ -223,7 +223,7 @@ static int dwc3_exynos_resume(struct device *dev)
        for (i = 0; i < exynos->num_clks; i++) {
                ret = clk_prepare_enable(exynos->clks[i]);
                if (ret) {
-                       while (--i > 0)
+                       while (i-- > 0)
                                clk_disable_unprepare(exynos->clks[i]);
                        return ret;
                }
index bed2ff4..6c9b76b 100644 (file)
@@ -1119,7 +1119,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
        unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
        unsigned int rem = length % maxp;
 
-       if (rem && usb_endpoint_dir_out(dep->endpoint.desc)) {
+       if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) {
                struct dwc3     *dwc = dep->dwc;
                struct dwc3_trb *trb;
 
index 660878a..b77f312 100644 (file)
@@ -2083,7 +2083,7 @@ static irqreturn_t net2272_irq(int irq, void *_dev)
 #if defined(PLX_PCI_RDK2)
        /* see if PCI int for us by checking irqstat */
        intcsr = readl(dev->rdk2.fpga_base_addr + RDK2_IRQSTAT);
-       if (!intcsr & (1 << NET2272_PCI_IRQ)) {
+       if (!(intcsr & (1 << NET2272_PCI_IRQ))) {
                spin_unlock(&dev->lock);
                return IRQ_NONE;
        }
index eae8b1b..ffe462a 100644 (file)
@@ -452,13 +452,10 @@ void musb_g_tx(struct musb *musb, u8 epnum)
        }
 
        if (request) {
-               u8      is_dma = 0;
-               bool    short_packet = false;
 
                trace_musb_req_tx(req);
 
                if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
-                       is_dma = 1;
                        csr |= MUSB_TXCSR_P_WZC_BITS;
                        csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN |
                                 MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_AUTOSET);
@@ -476,16 +473,8 @@ void musb_g_tx(struct musb *musb, u8 epnum)
                 */
                if ((request->zero && request->length)
                        && (request->length % musb_ep->packet_sz == 0)
-                       && (request->actual == request->length))
-                               short_packet = true;
+                       && (request->actual == request->length)) {
 
-               if ((musb_dma_inventra(musb) || musb_dma_ux500(musb)) &&
-                       (is_dma && (!dma->desired_mode ||
-                               (request->actual &
-                                       (musb_ep->packet_sz - 1)))))
-                               short_packet = true;
-
-               if (short_packet) {
                        /*
                         * On DMA completion, FIFO may not be
                         * available yet...
index a688f7f..5fc6825 100644 (file)
@@ -346,12 +346,10 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
                                channel->status = MUSB_DMA_STATUS_FREE;
 
                                /* completed */
-                               if ((devctl & MUSB_DEVCTL_HM)
-                                       && (musb_channel->transmit)
-                                       && ((channel->desired_mode == 0)
-                                           || (channel->actual_len &
-                                           (musb_channel->max_packet_sz - 1)))
-                                   ) {
+                               if (musb_channel->transmit &&
+                                       (!channel->desired_mode ||
+                                       (channel->actual_len %
+                                           musb_channel->max_packet_sz))) {
                                        u8  epnum  = musb_channel->epnum;
                                        int offset = musb->io.ep_offset(epnum,
                                                                    MUSB_TXCSR);
@@ -363,11 +361,14 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
                                         */
                                        musb_ep_select(mbase, epnum);
                                        txcsr = musb_readw(mbase, offset);
-                                       txcsr &= ~(MUSB_TXCSR_DMAENAB
+                                       if (channel->desired_mode == 1) {
+                                               txcsr &= ~(MUSB_TXCSR_DMAENAB
                                                        | MUSB_TXCSR_AUTOSET);
-                                       musb_writew(mbase, offset, txcsr);
-                                       /* Send out the packet */
-                                       txcsr &= ~MUSB_TXCSR_DMAMODE;
+                                               musb_writew(mbase, offset, txcsr);
+                                               /* Send out the packet */
+                                               txcsr &= ~MUSB_TXCSR_DMAMODE;
+                                               txcsr |= MUSB_TXCSR_DMAENAB;
+                                       }
                                        txcsr |=  MUSB_TXCSR_TXPKTRDY;
                                        musb_writew(mbase, offset, txcsr);
                                }
index d7312ee..91ea308 100644 (file)
@@ -21,7 +21,7 @@ config AB8500_USB
 
 config FSL_USB2_OTG
        bool "Freescale USB OTG Transceiver Driver"
-       depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM
+       depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM=y && PM
        depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
        select USB_PHY
        help
index 27bdb72..f5f0568 100644 (file)
@@ -61,9 +61,6 @@ static int am335x_phy_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       ret = usb_add_phy_dev(&am_phy->usb_phy_gen.phy);
-       if (ret)
-               return ret;
        am_phy->usb_phy_gen.phy.init = am335x_init;
        am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown;
 
@@ -82,7 +79,7 @@ static int am335x_phy_probe(struct platform_device *pdev)
        device_set_wakeup_enable(dev, false);
        phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false);
 
-       return 0;
+       return usb_add_phy_dev(&am_phy->usb_phy_gen.phy);
 }
 
 static int am335x_phy_remove(struct platform_device *pdev)
index 4bc29b5..f1c39a3 100644 (file)
@@ -2297,7 +2297,8 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
                                              pdo_pps_apdo_max_voltage(snk));
                port->pps_data.max_curr = min_pps_apdo_current(src, snk);
                port->pps_data.out_volt = min(port->pps_data.max_volt,
-                                             port->pps_data.out_volt);
+                                             max(port->pps_data.min_volt,
+                                                 port->pps_data.out_volt));
                port->pps_data.op_curr = min(port->pps_data.max_curr,
                                             port->pps_data.op_curr);
        }
index bca86bf..df51a35 100644 (file)
@@ -1337,7 +1337,8 @@ static int vhost_net_open(struct inode *inode, struct file *f)
                n->vqs[i].rx_ring = NULL;
                vhost_net_buf_init(&n->vqs[i].rxq);
        }
-       vhost_dev_init(dev, vqs, VHOST_NET_VQ_MAX);
+       vhost_dev_init(dev, vqs, VHOST_NET_VQ_MAX,
+                      UIO_MAXIOV + VHOST_NET_BATCH);
 
        vhost_poll_init(n->poll + VHOST_NET_VQ_TX, handle_tx_net, EPOLLOUT, dev);
        vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, EPOLLIN, dev);
index 344684f..23593cb 100644 (file)
@@ -1627,7 +1627,7 @@ static int vhost_scsi_open(struct inode *inode, struct file *f)
                vqs[i] = &vs->vqs[i].vq;
                vs->vqs[i].vq.handle_kick = vhost_scsi_handle_kick;
        }
-       vhost_dev_init(&vs->dev, vqs, VHOST_SCSI_MAX_VQ);
+       vhost_dev_init(&vs->dev, vqs, VHOST_SCSI_MAX_VQ, UIO_MAXIOV);
 
        vhost_scsi_init_inflight(vs, NULL);
 
index 15a216c..24a129f 100644 (file)
@@ -390,9 +390,9 @@ static long vhost_dev_alloc_iovecs(struct vhost_dev *dev)
                vq->indirect = kmalloc_array(UIO_MAXIOV,
                                             sizeof(*vq->indirect),
                                             GFP_KERNEL);
-               vq->log = kmalloc_array(UIO_MAXIOV, sizeof(*vq->log),
+               vq->log = kmalloc_array(dev->iov_limit, sizeof(*vq->log),
                                        GFP_KERNEL);
-               vq->heads = kmalloc_array(UIO_MAXIOV, sizeof(*vq->heads),
+               vq->heads = kmalloc_array(dev->iov_limit, sizeof(*vq->heads),
                                          GFP_KERNEL);
                if (!vq->indirect || !vq->log || !vq->heads)
                        goto err_nomem;
@@ -414,7 +414,7 @@ static void vhost_dev_free_iovecs(struct vhost_dev *dev)
 }
 
 void vhost_dev_init(struct vhost_dev *dev,
-                   struct vhost_virtqueue **vqs, int nvqs)
+                   struct vhost_virtqueue **vqs, int nvqs, int iov_limit)
 {
        struct vhost_virtqueue *vq;
        int i;
@@ -427,6 +427,7 @@ void vhost_dev_init(struct vhost_dev *dev,
        dev->iotlb = NULL;
        dev->mm = NULL;
        dev->worker = NULL;
+       dev->iov_limit = iov_limit;
        init_llist_head(&dev->work_list);
        init_waitqueue_head(&dev->wait);
        INIT_LIST_HEAD(&dev->read_list);
index 1b675da..9490e7d 100644 (file)
@@ -170,9 +170,11 @@ struct vhost_dev {
        struct list_head read_list;
        struct list_head pending_list;
        wait_queue_head_t wait;
+       int iov_limit;
 };
 
-void vhost_dev_init(struct vhost_dev *, struct vhost_virtqueue **vqs, int nvqs);
+void vhost_dev_init(struct vhost_dev *, struct vhost_virtqueue **vqs,
+                   int nvqs, int iov_limit);
 long vhost_dev_set_owner(struct vhost_dev *dev);
 bool vhost_dev_has_owner(struct vhost_dev *dev);
 long vhost_dev_check_owner(struct vhost_dev *);
index 3fbc068..bb5fc0e 100644 (file)
@@ -531,7 +531,7 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
        vsock->vqs[VSOCK_VQ_TX].handle_kick = vhost_vsock_handle_tx_kick;
        vsock->vqs[VSOCK_VQ_RX].handle_kick = vhost_vsock_handle_rx_kick;
 
-       vhost_dev_init(&vsock->dev, vqs, ARRAY_SIZE(vsock->vqs));
+       vhost_dev_init(&vsock->dev, vqs, ARRAY_SIZE(vsock->vqs), UIO_MAXIOV);
 
        file->private_data = vsock;
        spin_lock_init(&vsock->send_pkt_list_lock);
index cd7e755..a0b07c3 100644 (file)
@@ -152,7 +152,12 @@ struct vring_virtqueue {
                /* Available for packed ring */
                struct {
                        /* Actual memory layout for this queue. */
-                       struct vring_packed vring;
+                       struct {
+                               unsigned int num;
+                               struct vring_packed_desc *desc;
+                               struct vring_packed_desc_event *driver;
+                               struct vring_packed_desc_event *device;
+                       } vring;
 
                        /* Driver ring wrap counter. */
                        bool avail_wrap_counter;
@@ -1609,6 +1614,9 @@ static struct virtqueue *vring_create_virtqueue_packed(
                !context;
        vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX);
 
+       if (virtio_has_feature(vdev, VIRTIO_F_ORDER_PLATFORM))
+               vq->weak_barriers = false;
+
        vq->packed.ring_dma_addr = ring_dma_addr;
        vq->packed.driver_event_dma_addr = driver_event_dma_addr;
        vq->packed.device_event_dma_addr = device_event_dma_addr;
@@ -2079,6 +2087,9 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index,
                !context;
        vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX);
 
+       if (virtio_has_feature(vdev, VIRTIO_F_ORDER_PLATFORM))
+               vq->weak_barriers = false;
+
        vq->split.queue_dma_addr = 0;
        vq->split.queue_size_in_bytes = 0;
 
@@ -2213,6 +2224,8 @@ void vring_transport_features(struct virtio_device *vdev)
                        break;
                case VIRTIO_F_RING_PACKED:
                        break;
+               case VIRTIO_F_ORDER_PLATFORM:
+                       break;
                default:
                        /* We don't understand this bit. */
                        __virtio_clear_bit(vdev, i);
index b906ff7..aaaaf4d 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -1436,6 +1436,7 @@ static int aio_prep_rw(struct kiocb *req, const struct iocb *iocb)
        if (unlikely(!req->ki_filp))
                return -EBADF;
        req->ki_complete = aio_complete_rw;
+       req->private = NULL;
        req->ki_pos = iocb->aio_offset;
        req->ki_flags = iocb_flags(req->ki_filp);
        if (iocb->aio_flags & IOCB_FLAG_RESFD)
index d441244..28d9c2b 100644 (file)
@@ -596,7 +596,6 @@ int autofs_expire_run(struct super_block *sb,
        pkt.len = dentry->d_name.len;
        memcpy(pkt.name, dentry->d_name.name, pkt.len);
        pkt.name[pkt.len] = '\0';
-       dput(dentry);
 
        if (copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)))
                ret = -EFAULT;
@@ -609,6 +608,8 @@ int autofs_expire_run(struct super_block *sb,
        complete_all(&ino->expire_complete);
        spin_unlock(&sbi->fs_lock);
 
+       dput(dentry);
+
        return ret;
 }
 
index 0e8ea2d..078992e 100644 (file)
@@ -266,8 +266,10 @@ int autofs_fill_super(struct super_block *s, void *data, int silent)
        }
        root_inode = autofs_get_inode(s, S_IFDIR | 0755);
        root = d_make_root(root_inode);
-       if (!root)
+       if (!root) {
+               ret = -ENOMEM;
                goto fail_ino;
+       }
        pipe = NULL;
 
        root->d_fsdata = ino;
index d0078cb..7cde3f4 100644 (file)
@@ -42,14 +42,10 @@ static int load_script(struct linux_binprm *bprm)
        fput(bprm->file);
        bprm->file = NULL;
 
-       for (cp = bprm->buf+2;; cp++) {
-               if (cp >= bprm->buf + BINPRM_BUF_SIZE)
-                       return -ENOEXEC;
-               if (!*cp || (*cp == '\n'))
-                       break;
-       }
+       bprm->buf[BINPRM_BUF_SIZE - 1] = '\0';
+       if ((cp = strchr(bprm->buf, '\n')) == NULL)
+               cp = bprm->buf+BINPRM_BUF_SIZE-1;
        *cp = '\0';
-
        while (cp > bprm->buf) {
                cp--;
                if ((*cp == ' ') || (*cp == '\t'))
index f64aad6..5a6c39b 100644 (file)
@@ -968,6 +968,48 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,
        return 0;
 }
 
+static struct extent_buffer *alloc_tree_block_no_bg_flush(
+                                         struct btrfs_trans_handle *trans,
+                                         struct btrfs_root *root,
+                                         u64 parent_start,
+                                         const struct btrfs_disk_key *disk_key,
+                                         int level,
+                                         u64 hint,
+                                         u64 empty_size)
+{
+       struct btrfs_fs_info *fs_info = root->fs_info;
+       struct extent_buffer *ret;
+
+       /*
+        * If we are COWing a node/leaf from the extent, chunk, device or free
+        * space trees, make sure that we do not finish block group creation of
+        * pending block groups. We do this to avoid a deadlock.
+        * COWing can result in allocation of a new chunk, and flushing pending
+        * block groups (btrfs_create_pending_block_groups()) can be triggered
+        * when finishing allocation of a new chunk. Creation of a pending block
+        * group modifies the extent, chunk, device and free space trees,
+        * therefore we could deadlock with ourselves since we are holding a
+        * lock on an extent buffer that btrfs_create_pending_block_groups() may
+        * try to COW later.
+        * For similar reasons, we also need to delay flushing pending block
+        * groups when splitting a leaf or node, from one of those trees, since
+        * we are holding a write lock on it and its parent or when inserting a
+        * new root node for one of those trees.
+        */
+       if (root == fs_info->extent_root ||
+           root == fs_info->chunk_root ||
+           root == fs_info->dev_root ||
+           root == fs_info->free_space_root)
+               trans->can_flush_pending_bgs = false;
+
+       ret = btrfs_alloc_tree_block(trans, root, parent_start,
+                                    root->root_key.objectid, disk_key, level,
+                                    hint, empty_size);
+       trans->can_flush_pending_bgs = true;
+
+       return ret;
+}
+
 /*
  * does the dirty work in cow of a single block.  The parent block (if
  * supplied) is updated to point to the new cow copy.  The new buffer is marked
@@ -1015,28 +1057,8 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
        if ((root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) && parent)
                parent_start = parent->start;
 
-       /*
-        * If we are COWing a node/leaf from the extent, chunk, device or free
-        * space trees, make sure that we do not finish block group creation of
-        * pending block groups. We do this to avoid a deadlock.
-        * COWing can result in allocation of a new chunk, and flushing pending
-        * block groups (btrfs_create_pending_block_groups()) can be triggered
-        * when finishing allocation of a new chunk. Creation of a pending block
-        * group modifies the extent, chunk, device and free space trees,
-        * therefore we could deadlock with ourselves since we are holding a
-        * lock on an extent buffer that btrfs_create_pending_block_groups() may
-        * try to COW later.
-        */
-       if (root == fs_info->extent_root ||
-           root == fs_info->chunk_root ||
-           root == fs_info->dev_root ||
-           root == fs_info->free_space_root)
-               trans->can_flush_pending_bgs = false;
-
-       cow = btrfs_alloc_tree_block(trans, root, parent_start,
-                       root->root_key.objectid, &disk_key, level,
-                       search_start, empty_size);
-       trans->can_flush_pending_bgs = true;
+       cow = alloc_tree_block_no_bg_flush(trans, root, parent_start, &disk_key,
+                                          level, search_start, empty_size);
        if (IS_ERR(cow))
                return PTR_ERR(cow);
 
@@ -3345,8 +3367,8 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
        else
                btrfs_node_key(lower, &lower_key, 0);
 
-       c = btrfs_alloc_tree_block(trans, root, 0, root->root_key.objectid,
-                                  &lower_key, level, root->node->start, 0);
+       c = alloc_tree_block_no_bg_flush(trans, root, 0, &lower_key, level,
+                                        root->node->start, 0);
        if (IS_ERR(c))
                return PTR_ERR(c);
 
@@ -3475,8 +3497,8 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
        mid = (c_nritems + 1) / 2;
        btrfs_node_key(c, &disk_key, mid);
 
-       split = btrfs_alloc_tree_block(trans, root, 0, root->root_key.objectid,
-                       &disk_key, level, c->start, 0);
+       split = alloc_tree_block_no_bg_flush(trans, root, 0, &disk_key, level,
+                                            c->start, 0);
        if (IS_ERR(split))
                return PTR_ERR(split);
 
@@ -4260,8 +4282,8 @@ again:
        else
                btrfs_item_key(l, &disk_key, mid);
 
-       right = btrfs_alloc_tree_block(trans, root, 0, root->root_key.objectid,
-                       &disk_key, 0, l->start, 0);
+       right = alloc_tree_block_no_bg_flush(trans, root, 0, &disk_key, 0,
+                                            l->start, 0);
        if (IS_ERR(right))
                return PTR_ERR(right);
 
index c5586ff..0a3f122 100644 (file)
@@ -1621,6 +1621,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
                                flags | SB_RDONLY, device_name, data);
                        if (IS_ERR(mnt_root)) {
                                root = ERR_CAST(mnt_root);
+                               kfree(subvol_name);
                                goto out;
                        }
 
@@ -1630,12 +1631,14 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
                        if (error < 0) {
                                root = ERR_PTR(error);
                                mntput(mnt_root);
+                               kfree(subvol_name);
                                goto out;
                        }
                }
        }
        if (IS_ERR(mnt_root)) {
                root = ERR_CAST(mnt_root);
+               kfree(subvol_name);
                goto out;
        }
 
index 127fa15..4ec2b66 100644 (file)
@@ -850,14 +850,6 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
 
        btrfs_trans_release_chunk_metadata(trans);
 
-       if (lock && should_end_transaction(trans) &&
-           READ_ONCE(cur_trans->state) == TRANS_STATE_RUNNING) {
-               spin_lock(&info->trans_lock);
-               if (cur_trans->state == TRANS_STATE_RUNNING)
-                       cur_trans->state = TRANS_STATE_BLOCKED;
-               spin_unlock(&info->trans_lock);
-       }
-
        if (lock && READ_ONCE(cur_trans->state) == TRANS_STATE_BLOCKED) {
                if (throttle)
                        return btrfs_commit_transaction(trans);
@@ -1879,6 +1871,21 @@ static void cleanup_transaction(struct btrfs_trans_handle *trans, int err)
        kmem_cache_free(btrfs_trans_handle_cachep, trans);
 }
 
+/*
+ * Release reserved delayed ref space of all pending block groups of the
+ * transaction and remove them from the list
+ */
+static void btrfs_cleanup_pending_block_groups(struct btrfs_trans_handle *trans)
+{
+       struct btrfs_fs_info *fs_info = trans->fs_info;
+       struct btrfs_block_group_cache *block_group, *tmp;
+
+       list_for_each_entry_safe(block_group, tmp, &trans->new_bgs, bg_list) {
+               btrfs_delayed_refs_rsv_release(fs_info, 1);
+               list_del_init(&block_group->bg_list);
+       }
+}
+
 static inline int btrfs_start_delalloc_flush(struct btrfs_fs_info *fs_info)
 {
        /*
@@ -2270,6 +2277,7 @@ scrub_continue:
        btrfs_scrub_continue(fs_info);
 cleanup_transaction:
        btrfs_trans_release_metadata(trans);
+       btrfs_cleanup_pending_block_groups(trans);
        btrfs_trans_release_chunk_metadata(trans);
        trans->block_rsv = NULL;
        btrfs_warn(fs_info, "Skipping commit of aborted transaction.");
index 3e4f8f8..1556192 100644 (file)
@@ -957,11 +957,11 @@ static noinline struct btrfs_device *device_list_add(const char *path,
                else
                        fs_devices = alloc_fs_devices(disk_super->fsid, NULL);
 
-               fs_devices->fsid_change = fsid_change_in_progress;
-
                if (IS_ERR(fs_devices))
                        return ERR_CAST(fs_devices);
 
+               fs_devices->fsid_change = fsid_change_in_progress;
+
                mutex_lock(&fs_devices->device_list_mutex);
                list_add(&fs_devices->fs_list, &fs_uuids);
 
index 52d024b..48318fb 100644 (file)
@@ -200,6 +200,7 @@ __find_get_block_slow(struct block_device *bdev, sector_t block)
        struct buffer_head *head;
        struct page *page;
        int all_mapped = 1;
+       static DEFINE_RATELIMIT_STATE(last_warned, HZ, 1);
 
        index = block >> (PAGE_SHIFT - bd_inode->i_blkbits);
        page = find_get_page_flags(bd_mapping, index, FGP_ACCESSED);
@@ -227,15 +228,15 @@ __find_get_block_slow(struct block_device *bdev, sector_t block)
         * file io on the block device and getblk.  It gets dealt with
         * elsewhere, don't buffer_error if we had some unmapped buffers
         */
-       if (all_mapped) {
-               printk("__find_get_block_slow() failed. "
-                       "block=%llu, b_blocknr=%llu\n",
-                       (unsigned long long)block,
-                       (unsigned long long)bh->b_blocknr);
-               printk("b_state=0x%08lx, b_size=%zu\n",
-                       bh->b_state, bh->b_size);
-               printk("device %pg blocksize: %d\n", bdev,
-                       1 << bd_inode->i_blkbits);
+       ratelimit_set_flags(&last_warned, RATELIMIT_MSG_ON_RELEASE);
+       if (all_mapped && __ratelimit(&last_warned)) {
+               printk("__find_get_block_slow() failed. block=%llu, "
+                      "b_blocknr=%llu, b_state=0x%08lx, b_size=%zu, "
+                      "device %pg blocksize: %d\n",
+                      (unsigned long long)block,
+                      (unsigned long long)bh->b_blocknr,
+                      bh->b_state, bh->b_size, bdev,
+                      1 << bd_inode->i_blkbits);
        }
 out_unlock:
        spin_unlock(&bd_mapping->private_lock);
index d1f9c2f..7652551 100644 (file)
@@ -150,5 +150,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
 extern const struct export_operations cifs_export_ops;
 #endif /* CONFIG_CIFS_NFSD_EXPORT */
 
-#define CIFS_VERSION   "2.16"
+#define CIFS_VERSION   "2.17"
 #endif                         /* _CIFSFS_H */
index 2c7689f..659ce1b 100644 (file)
@@ -2696,6 +2696,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
 
                        rc = cifs_write_allocate_pages(wdata->pages, nr_pages);
                        if (rc) {
+                               kvfree(wdata->pages);
                                kfree(wdata);
                                add_credits_and_wake_if(server, credits, 0);
                                break;
@@ -2707,6 +2708,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
                        if (rc) {
                                for (i = 0; i < nr_pages; i++)
                                        put_page(wdata->pages[i]);
+                               kvfree(wdata->pages);
                                kfree(wdata);
                                add_credits_and_wake_if(server, credits, 0);
                                break;
@@ -3386,8 +3388,12 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
                        }
 
                        rc = cifs_read_allocate_pages(rdata, npages);
-                       if (rc)
-                               goto error;
+                       if (rc) {
+                               kvfree(rdata->pages);
+                               kfree(rdata);
+                               add_credits_and_wake_if(server, credits, 0);
+                               break;
+                       }
 
                        rdata->tailsz = PAGE_SIZE;
                }
@@ -3407,7 +3413,6 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
                if (!rdata->cfile->invalidHandle ||
                    !(rc = cifs_reopen_file(rdata->cfile, true)))
                        rc = server->ops->async_readv(rdata);
-error:
                if (rc) {
                        add_credits_and_wake_if(server, rdata->credits, 0);
                        kref_put(&rdata->refcount,
index 153238f..6f96e22 100644 (file)
@@ -866,7 +866,9 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
                                      FILE_READ_EA,
                                      FILE_FULL_EA_INFORMATION,
                                      SMB2_O_INFO_FILE,
-                                     SMB2_MAX_EA_BUF,
+                                     CIFSMaxBufSize -
+                                     MAX_SMB2_CREATE_RESPONSE_SIZE -
+                                     MAX_SMB2_CLOSE_RESPONSE_SIZE,
                                      &rsp_iov, &buftype, cifs_sb);
        if (rc) {
                /*
index 2ff209e..77b3aaa 100644 (file)
@@ -3241,8 +3241,17 @@ smb2_readv_callback(struct mid_q_entry *mid)
                rdata->mr = NULL;
        }
 #endif
-       if (rdata->result)
+       if (rdata->result && rdata->result != -ENODATA) {
                cifs_stats_fail_inc(tcon, SMB2_READ_HE);
+               trace_smb3_read_err(0 /* xid */,
+                                   rdata->cfile->fid.persistent_fid,
+                                   tcon->tid, tcon->ses->Suid, rdata->offset,
+                                   rdata->bytes, rdata->result);
+       } else
+               trace_smb3_read_done(0 /* xid */,
+                                    rdata->cfile->fid.persistent_fid,
+                                    tcon->tid, tcon->ses->Suid,
+                                    rdata->offset, rdata->got_bytes);
 
        queue_work(cifsiod_wq, &rdata->work);
        DeleteMidQEntry(mid);
@@ -3317,13 +3326,11 @@ smb2_async_readv(struct cifs_readdata *rdata)
        if (rc) {
                kref_put(&rdata->refcount, cifs_readdata_release);
                cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE);
-               trace_smb3_read_err(rc, 0 /* xid */, io_parms.persistent_fid,
-                                  io_parms.tcon->tid, io_parms.tcon->ses->Suid,
-                                  io_parms.offset, io_parms.length);
-       } else
-               trace_smb3_read_done(0 /* xid */, io_parms.persistent_fid,
-                                  io_parms.tcon->tid, io_parms.tcon->ses->Suid,
-                                  io_parms.offset, io_parms.length);
+               trace_smb3_read_err(0 /* xid */, io_parms.persistent_fid,
+                                   io_parms.tcon->tid,
+                                   io_parms.tcon->ses->Suid,
+                                   io_parms.offset, io_parms.length, rc);
+       }
 
        cifs_small_buf_release(buf);
        return rc;
@@ -3367,10 +3374,11 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
                if (rc != -ENODATA) {
                        cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE);
                        cifs_dbg(VFS, "Send error in read = %d\n", rc);
+                       trace_smb3_read_err(xid, req->PersistentFileId,
+                                           io_parms->tcon->tid, ses->Suid,
+                                           io_parms->offset, io_parms->length,
+                                           rc);
                }
-               trace_smb3_read_err(rc, xid, req->PersistentFileId,
-                                   io_parms->tcon->tid, ses->Suid,
-                                   io_parms->offset, io_parms->length);
                free_rsp_buf(resp_buftype, rsp_iov.iov_base);
                return rc == -ENODATA ? 0 : rc;
        } else
@@ -3459,8 +3467,17 @@ smb2_writev_callback(struct mid_q_entry *mid)
                wdata->mr = NULL;
        }
 #endif
-       if (wdata->result)
+       if (wdata->result) {
                cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
+               trace_smb3_write_err(0 /* no xid */,
+                                    wdata->cfile->fid.persistent_fid,
+                                    tcon->tid, tcon->ses->Suid, wdata->offset,
+                                    wdata->bytes, wdata->result);
+       } else
+               trace_smb3_write_done(0 /* no xid */,
+                                     wdata->cfile->fid.persistent_fid,
+                                     tcon->tid, tcon->ses->Suid,
+                                     wdata->offset, wdata->bytes);
 
        queue_work(cifsiod_wq, &wdata->work);
        DeleteMidQEntry(mid);
@@ -3602,10 +3619,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
                                     wdata->bytes, rc);
                kref_put(&wdata->refcount, release);
                cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
-       } else
-               trace_smb3_write_done(0 /* no xid */, req->PersistentFileId,
-                                    tcon->tid, tcon->ses->Suid, wdata->offset,
-                                    wdata->bytes);
+       }
 
 async_writev_out:
        cifs_small_buf_release(req);
@@ -3831,8 +3845,8 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
                    rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) {
                        srch_inf->endOfSearch = true;
                        rc = 0;
-               }
-               cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE);
+               } else
+                       cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE);
                goto qdir_exit;
        }
 
@@ -4427,8 +4441,8 @@ SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
        rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov);
        cifs_small_buf_release(req);
 
-       please_key_low = (__u64 *)req->LeaseKey;
-       please_key_high = (__u64 *)(req->LeaseKey+8);
+       please_key_low = (__u64 *)lease_key;
+       please_key_high = (__u64 *)(lease_key+8);
        if (rc) {
                cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
                trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid,
index 7a2d0a2..538e229 100644 (file)
@@ -84,8 +84,9 @@
 
 #define NUMBER_OF_SMB2_COMMANDS        0x0013
 
-/* 4 len + 52 transform hdr + 64 hdr + 56 create rsp */
-#define MAX_SMB2_HDR_SIZE 0x00b0
+/* 52 transform hdr + 64 hdr + 88 create rsp */
+#define SMB2_TRANSFORM_HEADER_SIZE 52
+#define MAX_SMB2_HDR_SIZE 204
 
 #define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe)
 #define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd)
@@ -648,6 +649,13 @@ struct smb2_create_req {
        __u8   Buffer[0];
 } __packed;
 
+/*
+ * Maximum size of a SMB2_CREATE response is 64 (smb2 header) +
+ * 88 (fixed part of create response) + 520 (path) + 150 (contexts) +
+ * 2 bytes of padding.
+ */
+#define MAX_SMB2_CREATE_RESPONSE_SIZE 824
+
 struct smb2_create_rsp {
        struct smb2_sync_hdr sync_hdr;
        __le16 StructureSize;   /* Must be 89 */
@@ -996,6 +1004,11 @@ struct smb2_close_req {
        __u64  VolatileFileId; /* opaque endianness */
 } __packed;
 
+/*
+ * Maximum size of a SMB2_CLOSE response is 64 (smb2 header) + 60 (data)
+ */
+#define MAX_SMB2_CLOSE_RESPONSE_SIZE 124
+
 struct smb2_close_rsp {
        struct smb2_sync_hdr sync_hdr;
        __le16 StructureSize; /* 60 */
@@ -1398,8 +1411,6 @@ struct smb2_file_link_info { /* encoding of request for level 11 */
        char   FileName[0];     /* Name to be assigned to new link */
 } __packed; /* level 11 Set */
 
-#define SMB2_MAX_EA_BUF 65536
-
 struct smb2_file_full_ea_info { /* encoding of response for level 15 */
        __le32 next_entry_offset;
        __u8   flags;
index 2593153..aac41ad 100644 (file)
@@ -119,6 +119,7 @@ struct dentry_stat_t dentry_stat = {
 
 static DEFINE_PER_CPU(long, nr_dentry);
 static DEFINE_PER_CPU(long, nr_dentry_unused);
+static DEFINE_PER_CPU(long, nr_dentry_negative);
 
 #if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
 
@@ -152,11 +153,22 @@ static long get_nr_dentry_unused(void)
        return sum < 0 ? 0 : sum;
 }
 
+static long get_nr_dentry_negative(void)
+{
+       int i;
+       long sum = 0;
+
+       for_each_possible_cpu(i)
+               sum += per_cpu(nr_dentry_negative, i);
+       return sum < 0 ? 0 : sum;
+}
+
 int proc_nr_dentry(struct ctl_table *table, int write, void __user *buffer,
                   size_t *lenp, loff_t *ppos)
 {
        dentry_stat.nr_dentry = get_nr_dentry();
        dentry_stat.nr_unused = get_nr_dentry_unused();
+       dentry_stat.nr_negative = get_nr_dentry_negative();
        return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
 }
 #endif
@@ -317,6 +329,8 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry)
        flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU);
        WRITE_ONCE(dentry->d_flags, flags);
        dentry->d_inode = NULL;
+       if (dentry->d_flags & DCACHE_LRU_LIST)
+               this_cpu_inc(nr_dentry_negative);
 }
 
 static void dentry_free(struct dentry *dentry)
@@ -371,6 +385,11 @@ static void dentry_unlink_inode(struct dentry * dentry)
  * The per-cpu "nr_dentry_unused" counters are updated with
  * the DCACHE_LRU_LIST bit.
  *
+ * The per-cpu "nr_dentry_negative" counters are only updated
+ * when deleted from or added to the per-superblock LRU list, not
+ * from/to the shrink list. That is to avoid an unneeded dec/inc
+ * pair when moving from LRU to shrink list in select_collect().
+ *
  * These helper functions make sure we always follow the
  * rules. d_lock must be held by the caller.
  */
@@ -380,6 +399,8 @@ static void d_lru_add(struct dentry *dentry)
        D_FLAG_VERIFY(dentry, 0);
        dentry->d_flags |= DCACHE_LRU_LIST;
        this_cpu_inc(nr_dentry_unused);
+       if (d_is_negative(dentry))
+               this_cpu_inc(nr_dentry_negative);
        WARN_ON_ONCE(!list_lru_add(&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
 }
 
@@ -388,6 +409,8 @@ static void d_lru_del(struct dentry *dentry)
        D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
        dentry->d_flags &= ~DCACHE_LRU_LIST;
        this_cpu_dec(nr_dentry_unused);
+       if (d_is_negative(dentry))
+               this_cpu_dec(nr_dentry_negative);
        WARN_ON_ONCE(!list_lru_del(&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
 }
 
@@ -418,6 +441,8 @@ static void d_lru_isolate(struct list_lru_one *lru, struct dentry *dentry)
        D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
        dentry->d_flags &= ~DCACHE_LRU_LIST;
        this_cpu_dec(nr_dentry_unused);
+       if (d_is_negative(dentry))
+               this_cpu_dec(nr_dentry_negative);
        list_lru_isolate(lru, &dentry->d_lru);
 }
 
@@ -426,6 +451,8 @@ static void d_lru_shrink_move(struct list_lru_one *lru, struct dentry *dentry,
 {
        D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
        dentry->d_flags |= DCACHE_SHRINK_LIST;
+       if (d_is_negative(dentry))
+               this_cpu_dec(nr_dentry_negative);
        list_lru_isolate_move(lru, &dentry->d_lru, list);
 }
 
@@ -1188,15 +1215,11 @@ static enum lru_status dentry_lru_isolate_shrink(struct list_head *item,
  */
 void shrink_dcache_sb(struct super_block *sb)
 {
-       long freed;
-
        do {
                LIST_HEAD(dispose);
 
-               freed = list_lru_walk(&sb->s_dentry_lru,
+               list_lru_walk(&sb->s_dentry_lru,
                        dentry_lru_isolate_shrink, &dispose, 1024);
-
-               this_cpu_sub(nr_dentry_unused, freed);
                shrink_dentry_list(&dispose);
        } while (list_lru_count(&sb->s_dentry_lru) > 0);
 }
@@ -1820,6 +1843,11 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
        WARN_ON(d_in_lookup(dentry));
 
        spin_lock(&dentry->d_lock);
+       /*
+        * Decrement negative dentry count if it was in the LRU list.
+        */
+       if (dentry->d_flags & DCACHE_LRU_LIST)
+               this_cpu_dec(nr_dentry_negative);
        hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
        raw_write_seqcount_begin(&dentry->d_seq);
        __d_set_inode_and_type(dentry, inode, add_flags);
index 13b0135..29c68c5 100644 (file)
@@ -324,7 +324,7 @@ static struct dentry *failed_creating(struct dentry *dentry)
        inode_unlock(d_inode(dentry->d_parent));
        dput(dentry);
        simple_release_fs(&debugfs_mount, &debugfs_mount_count);
-       return NULL;
+       return ERR_PTR(-ENOMEM);
 }
 
 static struct dentry *end_creating(struct dentry *dentry)
@@ -347,7 +347,7 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
        dentry = start_creating(name, parent);
 
        if (IS_ERR(dentry))
-               return NULL;
+               return dentry;
 
        inode = debugfs_get_inode(dentry->d_sb);
        if (unlikely(!inode))
@@ -386,7 +386,8 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
  * This function will return a pointer to a dentry if it succeeds.  This
  * pointer must be passed to the debugfs_remove() function when the file is
  * to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here.)  If an error occurs, %NULL will be returned.
+ * you are responsible here.)  If an error occurs, %ERR_PTR(-ERROR) will be
+ * returned.
  *
  * If debugfs is not enabled in the kernel, the value -%ENODEV will be
  * returned.
@@ -464,7 +465,8 @@ EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe);
  * This function will return a pointer to a dentry if it succeeds.  This
  * pointer must be passed to the debugfs_remove() function when the file is
  * to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here.)  If an error occurs, %NULL will be returned.
+ * you are responsible here.)  If an error occurs, %ERR_PTR(-ERROR) will be
+ * returned.
  *
  * If debugfs is not enabled in the kernel, the value -%ENODEV will be
  * returned.
@@ -495,7 +497,8 @@ EXPORT_SYMBOL_GPL(debugfs_create_file_size);
  * This function will return a pointer to a dentry if it succeeds.  This
  * pointer must be passed to the debugfs_remove() function when the file is
  * to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here.)  If an error occurs, %NULL will be returned.
+ * you are responsible here.)  If an error occurs, %ERR_PTR(-ERROR) will be
+ * returned.
  *
  * If debugfs is not enabled in the kernel, the value -%ENODEV will be
  * returned.
@@ -506,7 +509,7 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
        struct inode *inode;
 
        if (IS_ERR(dentry))
-               return NULL;
+               return dentry;
 
        inode = debugfs_get_inode(dentry->d_sb);
        if (unlikely(!inode))
@@ -545,7 +548,7 @@ struct dentry *debugfs_create_automount(const char *name,
        struct inode *inode;
 
        if (IS_ERR(dentry))
-               return NULL;
+               return dentry;
 
        inode = debugfs_get_inode(dentry->d_sb);
        if (unlikely(!inode))
@@ -581,8 +584,8 @@ EXPORT_SYMBOL(debugfs_create_automount);
  * This function will return a pointer to a dentry if it succeeds.  This
  * pointer must be passed to the debugfs_remove() function when the symbolic
  * link is to be removed (no automatic cleanup happens if your module is
- * unloaded, you are responsible here.)  If an error occurs, %NULL will be
- * returned.
+ * unloaded, you are responsible here.)  If an error occurs, %ERR_PTR(-ERROR)
+ * will be returned.
  *
  * If debugfs is not enabled in the kernel, the value -%ENODEV will be
  * returned.
@@ -594,12 +597,12 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
        struct inode *inode;
        char *link = kstrdup(target, GFP_KERNEL);
        if (!link)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        dentry = start_creating(name, parent);
        if (IS_ERR(dentry)) {
                kfree(link);
-               return NULL;
+               return dentry;
        }
 
        inode = debugfs_get_inode(dentry->d_sb);
@@ -787,6 +790,13 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
        struct dentry *dentry = NULL, *trap;
        struct name_snapshot old_name;
 
+       if (IS_ERR(old_dir))
+               return old_dir;
+       if (IS_ERR(new_dir))
+               return new_dir;
+       if (IS_ERR_OR_NULL(old_dentry))
+               return old_dentry;
+
        trap = lock_rename(new_dir, old_dir);
        /* Source or destination directories don't exist? */
        if (d_really_is_negative(old_dir) || d_really_is_negative(new_dir))
@@ -820,7 +830,9 @@ exit:
        if (dentry && !IS_ERR(dentry))
                dput(dentry);
        unlock_rename(new_dir, old_dir);
-       return NULL;
+       if (IS_ERR(dentry))
+               return dentry;
+       return ERR_PTR(-EINVAL);
 }
 EXPORT_SYMBOL_GPL(debugfs_rename);
 
index 76976d6..c98ad97 100644 (file)
@@ -1089,12 +1089,12 @@ static void sctp_connect_to_sock(struct connection *con)
         * since O_NONBLOCK argument in connect() function does not work here,
         * then, we should restore the default value of this attribute.
         */
-       kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv,
+       kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO_OLD, (char *)&tv,
                          sizeof(tv));
        result = sock->ops->connect(sock, (struct sockaddr *)&daddr, addr_len,
                                   0);
        memset(&tv, 0, sizeof(tv));
-       kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv,
+       kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO_OLD, (char *)&tv,
                          sizeof(tv));
 
        if (result == -EINPROGRESS)
index 8237701..d31b6c7 100644 (file)
@@ -21,8 +21,13 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused)
        spin_lock(&sb->s_inode_list_lock);
        list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
                spin_lock(&inode->i_lock);
+               /*
+                * We must skip inodes in unusual state. We may also skip
+                * inodes without pages but we deliberately won't in case
+                * we need to reschedule to avoid softlockups.
+                */
                if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) ||
-                   (inode->i_mapping->nrpages == 0)) {
+                   (inode->i_mapping->nrpages == 0 && !need_resched())) {
                        spin_unlock(&inode->i_lock);
                        continue;
                }
@@ -30,6 +35,7 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused)
                spin_unlock(&inode->i_lock);
                spin_unlock(&sb->s_inode_list_lock);
 
+               cond_resched();
                invalidate_mapping_pages(inode->i_mapping, 0, -1);
                iput(toput_inode);
                toput_inode = inode;
index 712f009..5508baa 100644 (file)
@@ -116,16 +116,8 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
                goto out;
        }
 
-       ret = file_write_and_wait_range(file, start, end);
-       if (ret)
-               return ret;
-
        if (!journal) {
-               struct writeback_control wbc = {
-                       .sync_mode = WB_SYNC_ALL
-               };
-
-               ret = ext4_write_inode(inode, &wbc);
+               ret = __generic_file_fsync(file, start, end, datasync);
                if (!ret)
                        ret = ext4_sync_parent(inode);
                if (test_opt(inode->i_sb, BARRIER))
@@ -133,6 +125,9 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
                goto out;
        }
 
+       ret = file_write_and_wait_range(file, start, end);
+       if (ret)
+               return ret;
        /*
         * data=writeback,ordered:
         *  The caller's filemap_fdatawrite()/wait will sync the data.
index a5e516a..809c0f2 100644 (file)
@@ -1742,7 +1742,6 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
        req->in.h.nodeid = outarg->nodeid;
        req->in.numargs = 2;
        req->in.argpages = 1;
-       req->page_descs[0].offset = offset;
        req->end = fuse_retrieve_end;
 
        index = outarg->offset >> PAGE_SHIFT;
@@ -1757,6 +1756,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
 
                this_num = min_t(unsigned, num, PAGE_SIZE - offset);
                req->pages[req->num_pages] = page;
+               req->page_descs[req->num_pages].offset = offset;
                req->page_descs[req->num_pages].length = this_num;
                req->num_pages++;
 
@@ -2077,8 +2077,10 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
 
        ret = fuse_dev_do_write(fud, &cs, len);
 
+       pipe_lock(pipe);
        for (idx = 0; idx < nbuf; idx++)
                pipe_buf_release(pipe, &bufs[idx]);
+       pipe_unlock(pipe);
 
 out:
        kvfree(bufs);
index ffaffe1..a59c16b 100644 (file)
@@ -1782,7 +1782,7 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req,
                spin_unlock(&fc->lock);
 
                dec_wb_stat(&bdi->wb, WB_WRITEBACK);
-               dec_node_page_state(page, NR_WRITEBACK_TEMP);
+               dec_node_page_state(new_req->pages[0], NR_WRITEBACK_TEMP);
                wb_writeout_inc(&bdi->wb);
                fuse_writepage_free(fc, new_req);
                fuse_request_free(new_req);
index 76baaa6..c2d4099 100644 (file)
@@ -628,6 +628,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns)
        get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));
        fc->pid_ns = get_pid_ns(task_active_pid_ns(current));
        fc->user_ns = get_user_ns(user_ns);
+       fc->max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ;
 }
 EXPORT_SYMBOL_GPL(fuse_conn_init);
 
@@ -1162,7 +1163,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
        fc->user_id = d.user_id;
        fc->group_id = d.group_id;
        fc->max_read = max_t(unsigned, 4096, d.max_read);
-       fc->max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ;
 
        /* Used by get_root_inode() */
        sb->s_fs_info = fc;
index f15b4c5..78510ab 100644 (file)
@@ -28,7 +28,6 @@
 #include "util.h"
 #include "trans.h"
 #include "dir.h"
-#include "lops.h"
 
 struct workqueue_struct *gfs2_freeze_wq;
 
index 5bfaf38..b8830fd 100644 (file)
@@ -733,7 +733,7 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
        lh->lh_crc = cpu_to_be32(crc);
 
        gfs2_log_write(sdp, page, sb->s_blocksize, 0, addr);
-       gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE | op_flags);
+       gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE, op_flags);
        log_flush_wait(sdp);
 }
 
@@ -810,7 +810,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
 
        gfs2_ordered_write(sdp);
        lops_before_commit(sdp, tr);
-       gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE);
+       gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE, 0);
 
        if (sdp->sd_log_head != sdp->sd_log_flush_head) {
                log_flush_wait(sdp);
index 94dcab6..2295042 100644 (file)
@@ -17,9 +17,7 @@
 #include <linux/bio.h>
 #include <linux/fs.h>
 #include <linux/list_sort.h>
-#include <linux/blkdev.h>
 
-#include "bmap.h"
 #include "dir.h"
 #include "gfs2.h"
 #include "incore.h"
@@ -195,6 +193,7 @@ static void gfs2_end_log_write_bh(struct gfs2_sbd *sdp, struct bio_vec *bvec,
 /**
  * gfs2_end_log_write - end of i/o to the log
  * @bio: The bio
+ * @error: Status of i/o request
  *
  * Each bio_vec contains either data from the pagecache or data
  * relating to the log itself. Here we iterate over the bio_vec
@@ -231,19 +230,20 @@ static void gfs2_end_log_write(struct bio *bio)
 /**
  * gfs2_log_submit_bio - Submit any pending log bio
  * @biop: Address of the bio pointer
- * @opf: REQ_OP | op_flags
+ * @op: REQ_OP
+ * @op_flags: req_flag_bits
  *
  * Submit any pending part-built or full bio to the block device. If
  * there is no pending bio, then this is a no-op.
  */
 
-void gfs2_log_submit_bio(struct bio **biop, int opf)
+void gfs2_log_submit_bio(struct bio **biop, int op, int op_flags)
 {
        struct bio *bio = *biop;
        if (bio) {
                struct gfs2_sbd *sdp = bio->bi_private;
                atomic_inc(&sdp->sd_log_in_flight);
-               bio->bi_opf = opf;
+               bio_set_op_attrs(bio, op, op_flags);
                submit_bio(bio);
                *biop = NULL;
        }
@@ -304,7 +304,7 @@ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno,
                nblk >>= sdp->sd_fsb2bb_shift;
                if (blkno == nblk && !flush)
                        return bio;
-               gfs2_log_submit_bio(biop, op);
+               gfs2_log_submit_bio(biop, op, 0);
        }
 
        *biop = gfs2_log_alloc_bio(sdp, blkno, end_io);
@@ -375,184 +375,6 @@ void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page)
                       gfs2_log_bmap(sdp));
 }
 
-/**
- * gfs2_end_log_read - end I/O callback for reads from the log
- * @bio: The bio
- *
- * Simply unlock the pages in the bio. The main thread will wait on them and
- * process them in order as necessary.
- */
-
-static void gfs2_end_log_read(struct bio *bio)
-{
-       struct page *page;
-       struct bio_vec *bvec;
-       int i;
-
-       bio_for_each_segment_all(bvec, bio, i) {
-               page = bvec->bv_page;
-               if (bio->bi_status) {
-                       int err = blk_status_to_errno(bio->bi_status);
-
-                       SetPageError(page);
-                       mapping_set_error(page->mapping, err);
-               }
-               unlock_page(page);
-       }
-
-       bio_put(bio);
-}
-
-/**
- * gfs2_jhead_pg_srch - Look for the journal head in a given page.
- * @jd: The journal descriptor
- * @page: The page to look in
- *
- * Returns: 1 if found, 0 otherwise.
- */
-
-static bool gfs2_jhead_pg_srch(struct gfs2_jdesc *jd,
-                             struct gfs2_log_header_host *head,
-                             struct page *page)
-{
-       struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
-       struct gfs2_log_header_host uninitialized_var(lh);
-       void *kaddr = kmap_atomic(page);
-       unsigned int offset;
-       bool ret = false;
-
-       for (offset = 0; offset < PAGE_SIZE; offset += sdp->sd_sb.sb_bsize) {
-               if (!__get_log_header(sdp, kaddr + offset, 0, &lh)) {
-                       if (lh.lh_sequence > head->lh_sequence)
-                               *head = lh;
-                       else {
-                               ret = true;
-                               break;
-                       }
-               }
-       }
-       kunmap_atomic(kaddr);
-       return ret;
-}
-
-/**
- * gfs2_jhead_process_page - Search/cleanup a page
- * @jd: The journal descriptor
- * @index: Index of the page to look into
- * @done: If set, perform only cleanup, else search and set if found.
- *
- * Find the page with 'index' in the journal's mapping. Search the page for
- * the journal head if requested (cleanup == false). Release refs on the
- * page so the page cache can reclaim it (put_page() twice). We grabbed a
- * reference on this page two times, first when we did a find_or_create_page()
- * to obtain the page to add it to the bio and second when we do a
- * find_get_page() here to get the page to wait on while I/O on it is being
- * completed.
- * This function is also used to free up a page we might've grabbed but not
- * used. Maybe we added it to a bio, but not submitted it for I/O. Or we
- * submitted the I/O, but we already found the jhead so we only need to drop
- * our references to the page.
- */
-
-static void gfs2_jhead_process_page(struct gfs2_jdesc *jd, unsigned long index,
-                                   struct gfs2_log_header_host *head,
-                                   bool *done)
-{
-       struct page *page;
-
-       page = find_get_page(jd->jd_inode->i_mapping, index);
-       wait_on_page_locked(page);
-
-       if (PageError(page))
-               *done = true;
-
-       if (!*done)
-               *done = gfs2_jhead_pg_srch(jd, head, page);
-
-       put_page(page); /* Once for find_get_page */
-       put_page(page); /* Once more for find_or_create_page */
-}
-
-/**
- * gfs2_find_jhead - find the head of a log
- * @jd: The journal descriptor
- * @head: The log descriptor for the head of the log is returned here
- *
- * Do a search of a journal by reading it in large chunks using bios and find
- * the valid log entry with the highest sequence number.  (i.e. the log head)
- *
- * Returns: 0 on success, errno otherwise
- */
-
-int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head)
-{
-       struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
-       struct address_space *mapping = jd->jd_inode->i_mapping;
-       struct gfs2_journal_extent *je;
-       u32 block, read_idx = 0, submit_idx = 0, index = 0;
-       int shift = PAGE_SHIFT - sdp->sd_sb.sb_bsize_shift;
-       int blocks_per_page = 1 << shift, sz, ret = 0;
-       struct bio *bio = NULL;
-       struct page *page;
-       bool done = false;
-       errseq_t since;
-
-       memset(head, 0, sizeof(*head));
-       if (list_empty(&jd->extent_list))
-               gfs2_map_journal_extents(sdp, jd);
-
-       since = filemap_sample_wb_err(mapping);
-       list_for_each_entry(je, &jd->extent_list, list) {
-               for (block = 0; block < je->blocks; block += blocks_per_page) {
-                       index = (je->lblock + block) >> shift;
-
-                       page = find_or_create_page(mapping, index, GFP_NOFS);
-                       if (!page) {
-                               ret = -ENOMEM;
-                               done = true;
-                               goto out;
-                       }
-
-                       if (bio) {
-                               sz = bio_add_page(bio, page, PAGE_SIZE, 0);
-                               if (sz == PAGE_SIZE)
-                                       goto page_added;
-                               submit_idx = index;
-                               submit_bio(bio);
-                               bio = NULL;
-                       }
-
-                       bio = gfs2_log_alloc_bio(sdp,
-                                                je->dblock + (index << shift),
-                                                gfs2_end_log_read);
-                       bio->bi_opf = REQ_OP_READ;
-                       sz = bio_add_page(bio, page, PAGE_SIZE, 0);
-                       gfs2_assert_warn(sdp, sz == PAGE_SIZE);
-
-page_added:
-                       if (submit_idx <= read_idx + BIO_MAX_PAGES) {
-                               /* Keep at least one bio in flight */
-                               continue;
-                       }
-
-                       gfs2_jhead_process_page(jd, read_idx++, head, &done);
-                       if (done)
-                               goto out;  /* found */
-               }
-       }
-
-out:
-       if (bio)
-               submit_bio(bio);
-       while (read_idx <= index)
-               gfs2_jhead_process_page(jd, read_idx++, head, &done);
-
-       if (!ret)
-               ret = filemap_check_wb_err(mapping, since);
-
-       return ret;
-}
-
 static struct page *gfs2_get_log_desc(struct gfs2_sbd *sdp, u32 ld_type,
                                      u32 ld_length, u32 ld_data1)
 {
index 331160f..711c4d8 100644 (file)
@@ -30,10 +30,8 @@ extern u64 gfs2_log_bmap(struct gfs2_sbd *sdp);
 extern void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
                           unsigned size, unsigned offset, u64 blkno);
 extern void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page);
-extern void gfs2_log_submit_bio(struct bio **biop, int opf);
+extern void gfs2_log_submit_bio(struct bio **biop, int op, int op_flags);
 extern void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh);
-extern int gfs2_find_jhead(struct gfs2_jdesc *jd,
-                          struct gfs2_log_header_host *head);
 
 static inline unsigned int buf_limit(struct gfs2_sbd *sdp)
 {
index 1179763..b041cb8 100644 (file)
@@ -41,7 +41,6 @@
 #include "dir.h"
 #include "meta_io.h"
 #include "trace_gfs2.h"
-#include "lops.h"
 
 #define DO 0
 #define UNDO 1
index 7389e44..2dac430 100644 (file)
@@ -182,6 +182,129 @@ static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk,
 }
 
 /**
+ * find_good_lh - find a good log header
+ * @jd: the journal
+ * @blk: the segment to start searching from
+ * @lh: the log header to fill in
+ * @forward: if true search forward in the log, else search backward
+ *
+ * Call get_log_header() to get a log header for a segment, but if the
+ * segment is bad, either scan forward or backward until we find a good one.
+ *
+ * Returns: errno
+ */
+
+static int find_good_lh(struct gfs2_jdesc *jd, unsigned int *blk,
+                       struct gfs2_log_header_host *head)
+{
+       unsigned int orig_blk = *blk;
+       int error;
+
+       for (;;) {
+               error = get_log_header(jd, *blk, head);
+               if (error <= 0)
+                       return error;
+
+               if (++*blk == jd->jd_blocks)
+                       *blk = 0;
+
+               if (*blk == orig_blk) {
+                       gfs2_consist_inode(GFS2_I(jd->jd_inode));
+                       return -EIO;
+               }
+       }
+}
+
+/**
+ * jhead_scan - make sure we've found the head of the log
+ * @jd: the journal
+ * @head: this is filled in with the log descriptor of the head
+ *
+ * At this point, seg and lh should be either the head of the log or just
+ * before.  Scan forward until we find the head.
+ *
+ * Returns: errno
+ */
+
+static int jhead_scan(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head)
+{
+       unsigned int blk = head->lh_blkno;
+       struct gfs2_log_header_host lh;
+       int error;
+
+       for (;;) {
+               if (++blk == jd->jd_blocks)
+                       blk = 0;
+
+               error = get_log_header(jd, blk, &lh);
+               if (error < 0)
+                       return error;
+               if (error == 1)
+                       continue;
+
+               if (lh.lh_sequence == head->lh_sequence) {
+                       gfs2_consist_inode(GFS2_I(jd->jd_inode));
+                       return -EIO;
+               }
+               if (lh.lh_sequence < head->lh_sequence)
+                       break;
+
+               *head = lh;
+       }
+
+       return 0;
+}
+
+/**
+ * gfs2_find_jhead - find the head of a log
+ * @jd: the journal
+ * @head: the log descriptor for the head of the log is returned here
+ *
+ * Do a binary search of a journal and find the valid log entry with the
+ * highest sequence number.  (i.e. the log head)
+ *
+ * Returns: errno
+ */
+
+int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head)
+{
+       struct gfs2_log_header_host lh_1, lh_m;
+       u32 blk_1, blk_2, blk_m;
+       int error;
+
+       blk_1 = 0;
+       blk_2 = jd->jd_blocks - 1;
+
+       for (;;) {
+               blk_m = (blk_1 + blk_2) / 2;
+
+               error = find_good_lh(jd, &blk_1, &lh_1);
+               if (error)
+                       return error;
+
+               error = find_good_lh(jd, &blk_m, &lh_m);
+               if (error)
+                       return error;
+
+               if (blk_1 == blk_m || blk_m == blk_2)
+                       break;
+
+               if (lh_1.lh_sequence <= lh_m.lh_sequence)
+                       blk_1 = blk_m;
+               else
+                       blk_2 = blk_m;
+       }
+
+       error = jhead_scan(jd, &lh_1);
+       if (error)
+               return error;
+
+       *head = lh_1;
+
+       return error;
+}
+
+/**
  * foreach_descriptor - go through the active part of the log
  * @jd: the journal
  * @start: the first log header in the active region
index 99575ab..11d8124 100644 (file)
@@ -27,6 +27,8 @@ extern int gfs2_revoke_add(struct gfs2_jdesc *jd, u64 blkno, unsigned int where)
 extern int gfs2_revoke_check(struct gfs2_jdesc *jd, u64 blkno, unsigned int where);
 extern void gfs2_revoke_clean(struct gfs2_jdesc *jd);
 
+extern int gfs2_find_jhead(struct gfs2_jdesc *jd,
+                   struct gfs2_log_header_host *head);
 extern int gfs2_recover_journal(struct gfs2_jdesc *gfs2_jd, bool wait);
 extern void gfs2_recover_func(struct work_struct *work);
 extern int __get_log_header(struct gfs2_sbd *sdp,
index 831d7cb..17a8d3b 100644 (file)
@@ -1780,9 +1780,9 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 *minext,
                        goto next_iter;
                }
                if (ret == -E2BIG) {
-                       n += rbm->bii - initial_bii;
                        rbm->bii = 0;
                        rbm->offset = 0;
+                       n += (rbm->bii - initial_bii);
                        goto res_covered_end_of_rgrp;
                }
                return ret;
index d4b11c9..ca71163 100644 (file)
@@ -45,7 +45,6 @@
 #include "util.h"
 #include "sys.h"
 #include "xattr.h"
-#include "lops.h"
 
 #define args_neq(a1, a2, x) ((a1)->ar_##x != (a2)->ar_##x)
 
index 0cd47fe..73432e6 100644 (file)
@@ -730,11 +730,8 @@ static enum lru_status inode_lru_isolate(struct list_head *item,
                return LRU_REMOVED;
        }
 
-       /*
-        * Recently referenced inodes and inodes with many attached pages
-        * get one more pass.
-        */
-       if (inode->i_state & I_REFERENCED || inode->i_data.nrpages > 1) {
+       /* recently referenced inodes get one more pass */
+       if (inode->i_state & I_REFERENCED) {
                inode->i_state &= ~I_REFERENCED;
                spin_unlock(&inode->i_lock);
                return LRU_ROTATE;
index a3088fa..897c602 100644 (file)
@@ -116,6 +116,12 @@ iomap_page_create(struct inode *inode, struct page *page)
        atomic_set(&iop->read_count, 0);
        atomic_set(&iop->write_count, 0);
        bitmap_zero(iop->uptodate, PAGE_SIZE / SECTOR_SIZE);
+
+       /*
+        * migrate_page_move_mapping() assumes that pages with private data have
+        * their count elevated by 1.
+        */
+       get_page(page);
        set_page_private(page, (unsigned long)iop);
        SetPagePrivate(page);
        return iop;
@@ -132,6 +138,7 @@ iomap_page_release(struct page *page)
        WARN_ON_ONCE(atomic_read(&iop->write_count));
        ClearPagePrivate(page);
        set_page_private(page, 0);
+       put_page(page);
        kfree(iop);
 }
 
@@ -569,8 +576,10 @@ iomap_migrate_page(struct address_space *mapping, struct page *newpage,
 
        if (page_has_private(page)) {
                ClearPagePrivate(page);
+               get_page(newpage);
                set_page_private(newpage, page_private(page));
                set_page_private(page, 0);
+               put_page(page);
                SetPagePrivate(newpage);
        }
 
@@ -1804,6 +1813,7 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
        loff_t pos = iocb->ki_pos, start = pos;
        loff_t end = iocb->ki_pos + count - 1, ret = 0;
        unsigned int flags = IOMAP_DIRECT;
+       bool wait_for_completion = is_sync_kiocb(iocb);
        struct blk_plug plug;
        struct iomap_dio *dio;
 
@@ -1823,7 +1833,6 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
        dio->end_io = end_io;
        dio->error = 0;
        dio->flags = 0;
-       dio->wait_for_completion = is_sync_kiocb(iocb);
 
        dio->submit.iter = iter;
        dio->submit.waiter = current;
@@ -1878,7 +1887,7 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
                dio_warn_stale_pagecache(iocb->ki_filp);
        ret = 0;
 
-       if (iov_iter_rw(iter) == WRITE && !dio->wait_for_completion &&
+       if (iov_iter_rw(iter) == WRITE && !wait_for_completion &&
            !inode->i_sb->s_dio_done_wq) {
                ret = sb_init_dio_done_wq(inode->i_sb);
                if (ret < 0)
@@ -1894,7 +1903,7 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
                if (ret <= 0) {
                        /* magic error code to fall back to buffered I/O */
                        if (ret == -ENOTBLK) {
-                               dio->wait_for_completion = true;
+                               wait_for_completion = true;
                                ret = 0;
                        }
                        break;
@@ -1916,8 +1925,24 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
        if (dio->flags & IOMAP_DIO_WRITE_FUA)
                dio->flags &= ~IOMAP_DIO_NEED_SYNC;
 
+       /*
+        * We are about to drop our additional submission reference, which
+        * might be the last reference to the dio.  There are three three
+        * different ways we can progress here:
+        *
+        *  (a) If this is the last reference we will always complete and free
+        *      the dio ourselves.
+        *  (b) If this is not the last reference, and we serve an asynchronous
+        *      iocb, we must never touch the dio after the decrement, the
+        *      I/O completion handler will complete and free it.
+        *  (c) If this is not the last reference, but we serve a synchronous
+        *      iocb, the I/O completion handler will wake us up on the drop
+        *      of the final reference, and we will complete and free it here
+        *      after we got woken by the I/O completion handler.
+        */
+       dio->wait_for_completion = wait_for_completion;
        if (!atomic_dec_and_test(&dio->ref)) {
-               if (!dio->wait_for_completion)
+               if (!wait_for_completion)
                        return -EIOCBQUEUED;
 
                for (;;) {
@@ -1934,9 +1959,7 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
                __set_current_state(TASK_RUNNING);
        }
 
-       ret = iomap_dio_complete(dio);
-
-       return ret;
+       return iomap_dio_complete(dio);
 
 out_free_dio:
        kfree(dio);
index 22ce3c8..0570391 100644 (file)
@@ -1895,6 +1895,11 @@ static int nfs_parse_devname(const char *dev_name,
        size_t len;
        char *end;
 
+       if (unlikely(!dev_name || !*dev_name)) {
+               dfprintk(MOUNT, "NFS: device name not specified\n");
+               return -EINVAL;
+       }
+
        /* Is the host name protected with square brakcets? */
        if (*dev_name == '[') {
                end = strchr(++dev_name, ']');
index 5a0bbf9..f12cb31 100644 (file)
@@ -621,11 +621,12 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
        nfs_set_page_writeback(page);
        WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags));
 
-       ret = 0;
+       ret = req->wb_context->error;
        /* If there is a fatal error that covers this write, just exit */
-       if (nfs_error_is_fatal_on_server(req->wb_context->error))
+       if (nfs_error_is_fatal_on_server(ret))
                goto out_launder;
 
+       ret = 0;
        if (!nfs_pageio_add_request(pgio, req)) {
                ret = pgio->pg_error;
                /*
@@ -635,9 +636,9 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
                        nfs_context_set_write_error(req->wb_context, ret);
                        if (nfs_error_is_fatal_on_server(ret))
                                goto out_launder;
-               }
+               } else
+                       ret = -EAGAIN;
                nfs_redirty_request(req);
-               ret = -EAGAIN;
        } else
                nfs_add_stats(page_file_mapping(page)->host,
                                NFSIOS_WRITEPAGES, 1);
index 9824e32..7dc98e1 100644 (file)
@@ -557,9 +557,11 @@ __be32 nfsd4_clone_file_range(struct file *src, u64 src_pos, struct file *dst,
        loff_t cloned;
 
        cloned = vfs_clone_file_range(src, src_pos, dst, dst_pos, count, 0);
+       if (cloned < 0)
+               return nfserrno(cloned);
        if (count && cloned != count)
-               cloned = -EINVAL;
-       return nfserrno(cloned < 0 ? cloned : 0);
+               return nfserrno(-EINVAL);
+       return 0;
 }
 
 ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
index 8ae1094..e39bac9 100644 (file)
@@ -256,7 +256,7 @@ struct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,
                inode = proc_get_inode(dir->i_sb, de);
                if (!inode)
                        return ERR_PTR(-ENOMEM);
-               d_set_d_op(dentry, &proc_misc_dentry_ops);
+               d_set_d_op(dentry, de->proc_dops);
                return d_splice_alias(inode, dentry);
        }
        read_unlock(&proc_subdir_lock);
@@ -429,6 +429,8 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
        INIT_LIST_HEAD(&ent->pde_openers);
        proc_set_user(ent, (*parent)->uid, (*parent)->gid);
 
+       ent->proc_dops = &proc_misc_dentry_ops;
+
 out:
        return ent;
 }
index 5185d7f..95b1419 100644 (file)
@@ -44,6 +44,7 @@ struct proc_dir_entry {
        struct completion *pde_unload_completion;
        const struct inode_operations *proc_iops;
        const struct file_operations *proc_fops;
+       const struct dentry_operations *proc_dops;
        union {
                const struct seq_operations *seq_ops;
                int (*single_show)(struct seq_file *, void *);
index d5e0fcb..a7b1243 100644 (file)
@@ -38,6 +38,22 @@ static struct net *get_proc_net(const struct inode *inode)
        return maybe_get_net(PDE_NET(PDE(inode)));
 }
 
+static int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags)
+{
+       return 0;
+}
+
+static const struct dentry_operations proc_net_dentry_ops = {
+       .d_revalidate   = proc_net_d_revalidate,
+       .d_delete       = always_delete_dentry,
+};
+
+static void pde_force_lookup(struct proc_dir_entry *pde)
+{
+       /* /proc/net/ entries can be changed under us by setns(CLONE_NEWNET) */
+       pde->proc_dops = &proc_net_dentry_ops;
+}
+
 static int seq_open_net(struct inode *inode, struct file *file)
 {
        unsigned int state_size = PDE(inode)->state_size;
@@ -90,6 +106,7 @@ struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode,
        p = proc_create_reg(name, mode, &parent, data);
        if (!p)
                return NULL;
+       pde_force_lookup(p);
        p->proc_fops = &proc_net_seq_fops;
        p->seq_ops = ops;
        p->state_size = state_size;
@@ -133,6 +150,7 @@ struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode
        p = proc_create_reg(name, mode, &parent, data);
        if (!p)
                return NULL;
+       pde_force_lookup(p);
        p->proc_fops = &proc_net_seq_fops;
        p->seq_ops = ops;
        p->state_size = state_size;
@@ -181,6 +199,7 @@ struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
        p = proc_create_reg(name, mode, &parent, data);
        if (!p)
                return NULL;
+       pde_force_lookup(p);
        p->proc_fops = &proc_net_single_fops;
        p->single_show = show;
        return proc_register(parent, p);
@@ -223,6 +242,7 @@ struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mo
        p = proc_create_reg(name, mode, &parent, data);
        if (!p)
                return NULL;
+       pde_force_lookup(p);
        p->proc_fops = &proc_net_single_fops;
        p->single_show = show;
        p->write = write;
index f0ec9ed..85b0ef8 100644 (file)
@@ -423,7 +423,7 @@ struct mem_size_stats {
 };
 
 static void smaps_account(struct mem_size_stats *mss, struct page *page,
-               bool compound, bool young, bool dirty)
+               bool compound, bool young, bool dirty, bool locked)
 {
        int i, nr = compound ? 1 << compound_order(page) : 1;
        unsigned long size = nr * PAGE_SIZE;
@@ -450,24 +450,31 @@ static void smaps_account(struct mem_size_stats *mss, struct page *page,
                else
                        mss->private_clean += size;
                mss->pss += (u64)size << PSS_SHIFT;
+               if (locked)
+                       mss->pss_locked += (u64)size << PSS_SHIFT;
                return;
        }
 
        for (i = 0; i < nr; i++, page++) {
                int mapcount = page_mapcount(page);
+               unsigned long pss = (PAGE_SIZE << PSS_SHIFT);
 
                if (mapcount >= 2) {
                        if (dirty || PageDirty(page))
                                mss->shared_dirty += PAGE_SIZE;
                        else
                                mss->shared_clean += PAGE_SIZE;
-                       mss->pss += (PAGE_SIZE << PSS_SHIFT) / mapcount;
+                       mss->pss += pss / mapcount;
+                       if (locked)
+                               mss->pss_locked += pss / mapcount;
                } else {
                        if (dirty || PageDirty(page))
                                mss->private_dirty += PAGE_SIZE;
                        else
                                mss->private_clean += PAGE_SIZE;
-                       mss->pss += PAGE_SIZE << PSS_SHIFT;
+                       mss->pss += pss;
+                       if (locked)
+                               mss->pss_locked += pss;
                }
        }
 }
@@ -490,6 +497,7 @@ static void smaps_pte_entry(pte_t *pte, unsigned long addr,
 {
        struct mem_size_stats *mss = walk->private;
        struct vm_area_struct *vma = walk->vma;
+       bool locked = !!(vma->vm_flags & VM_LOCKED);
        struct page *page = NULL;
 
        if (pte_present(*pte)) {
@@ -532,7 +540,7 @@ static void smaps_pte_entry(pte_t *pte, unsigned long addr,
        if (!page)
                return;
 
-       smaps_account(mss, page, false, pte_young(*pte), pte_dirty(*pte));
+       smaps_account(mss, page, false, pte_young(*pte), pte_dirty(*pte), locked);
 }
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
@@ -541,6 +549,7 @@ static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr,
 {
        struct mem_size_stats *mss = walk->private;
        struct vm_area_struct *vma = walk->vma;
+       bool locked = !!(vma->vm_flags & VM_LOCKED);
        struct page *page;
 
        /* FOLL_DUMP will return -EFAULT on huge zero page */
@@ -555,7 +564,7 @@ static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr,
                /* pass */;
        else
                VM_BUG_ON_PAGE(1, page);
-       smaps_account(mss, page, true, pmd_young(*pmd), pmd_dirty(*pmd));
+       smaps_account(mss, page, true, pmd_young(*pmd), pmd_dirty(*pmd), locked);
 }
 #else
 static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr,
@@ -737,11 +746,8 @@ static void smap_gather_stats(struct vm_area_struct *vma,
                }
        }
 #endif
-
        /* mmap_sem is held in m_start */
        walk_page_vma(vma, &smaps_walk);
-       if (vma->vm_flags & VM_LOCKED)
-               mss->pss_locked += mss->pss;
 }
 
 #define SEQ_PUT_DEC(str, val) \
index 1c8eecf..6acf1bf 100644 (file)
@@ -768,18 +768,23 @@ xrep_findroot_block(
                if (!uuid_equal(&btblock->bb_u.s.bb_uuid,
                                &mp->m_sb.sb_meta_uuid))
                        goto out;
+               /*
+                * Read verifiers can reference b_ops, so we set the pointer
+                * here.  If the verifier fails we'll reset the buffer state
+                * to what it was before we touched the buffer.
+                */
+               bp->b_ops = fab->buf_ops;
                fab->buf_ops->verify_read(bp);
                if (bp->b_error) {
+                       bp->b_ops = NULL;
                        bp->b_error = 0;
                        goto out;
                }
 
                /*
                 * Some read verifiers will (re)set b_ops, so we must be
-                * careful not to blow away any such assignment.
+                * careful not to change b_ops after running the verifier.
                 */
-               if (!bp->b_ops)
-                       bp->b_ops = fab->buf_ops;
        }
 
        /*
index 338b9d9..d9048bc 100644 (file)
@@ -449,6 +449,7 @@ xfs_map_blocks(
        }
 
        wpc->imap = imap;
+       xfs_trim_extent_eof(&wpc->imap, ip);
        trace_xfs_map_blocks_found(ip, offset, count, wpc->io_type, &imap);
        return 0;
 allocate_blocks:
@@ -459,6 +460,7 @@ allocate_blocks:
        ASSERT(whichfork == XFS_COW_FORK || cow_fsb == NULLFILEOFF ||
               imap.br_startoff + imap.br_blockcount <= cow_fsb);
        wpc->imap = imap;
+       xfs_trim_extent_eof(&wpc->imap, ip);
        trace_xfs_map_blocks_alloc(ip, offset, count, wpc->io_type, &imap);
        return 0;
 }
index eedc5e0..4f5f2ff 100644 (file)
@@ -776,10 +776,26 @@ _xfs_buf_read(
 }
 
 /*
+ * Set buffer ops on an unchecked buffer and validate it, if possible.
+ *
  * If the caller passed in an ops structure and the buffer doesn't have ops
  * assigned, set the ops and use them to verify the contents.  If the contents
  * cannot be verified, we'll clear XBF_DONE.  We assume the buffer has no
  * recorded errors and is already in XBF_DONE state.
+ *
+ * Under normal operations, every in-core buffer must have buffer ops assigned
+ * to them when the buffer is read in from disk so that we can validate the
+ * metadata.
+ *
+ * However, there are two scenarios where one can encounter in-core buffers
+ * that don't have buffer ops.  The first is during log recovery of buffers on
+ * a V4 filesystem, though these buffers are purged at the end of recovery.
+ *
+ * The other is online repair, which tries to match arbitrary metadata blocks
+ * with btree types in order to find the root.  If online repair doesn't match
+ * the buffer with /any/ btree type, the buffer remains in memory in DONE state
+ * with no ops, and a subsequent read_buf call from elsewhere will not set the
+ * ops.  This function helps us fix this situation.
  */
 int
 xfs_buf_ensure_ops(
@@ -1536,8 +1552,7 @@ __xfs_buf_submit(
                xfs_buf_ioerror(bp, -EIO);
                bp->b_flags &= ~XBF_DONE;
                xfs_buf_stale(bp);
-               if (bp->b_flags & XBF_ASYNC)
-                       xfs_buf_ioend(bp);
+               xfs_buf_ioend(bp);
                return -EIO;
        }
 
index b53be41..04f7ac3 100644 (file)
 #define IMX8MQ_CLK_VPU_G2_ROOT                 241
 
 /* SCCG PLL GATE */
-#define IMX8MQ_SYS1_PLL_OUT                    232
+#define IMX8MQ_SYS1_PLL_OUT                    242
 #define IMX8MQ_SYS2_PLL_OUT                    243
 #define IMX8MQ_SYS3_PLL_OUT                    244
 #define IMX8MQ_DRAM_PLL_OUT                    245
 /* txesc clock */
 #define IMX8MQ_CLK_DSI_IPG_DIV                  256
 
-#define IMX8MQ_CLK_TMU_ROOT                    265
+#define IMX8MQ_CLK_TMU_ROOT                    257
 
 /* Display root clocks */
-#define IMX8MQ_CLK_DISP_AXI_ROOT               266
-#define IMX8MQ_CLK_DISP_APB_ROOT               267
-#define IMX8MQ_CLK_DISP_RTRM_ROOT              268
+#define IMX8MQ_CLK_DISP_AXI_ROOT               258
+#define IMX8MQ_CLK_DISP_APB_ROOT               259
+#define IMX8MQ_CLK_DISP_RTRM_ROOT              260
 
-#define IMX8MQ_CLK_OCOTP_ROOT                  269
+#define IMX8MQ_CLK_OCOTP_ROOT                  261
 
-#define IMX8MQ_CLK_DRAM_ALT_ROOT               270
-#define IMX8MQ_CLK_DRAM_CORE                   271
+#define IMX8MQ_CLK_DRAM_ALT_ROOT               262
+#define IMX8MQ_CLK_DRAM_CORE                   263
 
-#define IMX8MQ_CLK_MU_ROOT                     272
-#define IMX8MQ_VIDEO2_PLL_OUT                  273
+#define IMX8MQ_CLK_MU_ROOT                     264
+#define IMX8MQ_VIDEO2_PLL_OUT                  265
 
-#define IMX8MQ_CLK_CLKO2                       274
+#define IMX8MQ_CLK_CLKO2                       266
 
-#define IMX8MQ_CLK_NAND_USDHC_BUS_RAWNAND_CLK  275
+#define IMX8MQ_CLK_NAND_USDHC_BUS_RAWNAND_CLK  267
 
-#define IMX8MQ_CLK_END                         276
+#define IMX8MQ_CLK_END                         268
 #endif /* __DT_BINDINGS_CLOCK_IMX8MQ_H */
index 7b24fc7..228a5e2 100644 (file)
@@ -71,7 +71,6 @@
 #define MMP2_CLK_CCIC1_MIX             117
 #define MMP2_CLK_CCIC1_PHY             118
 #define MMP2_CLK_CCIC1_SPHY            119
-#define MMP2_CLK_SP                    120
 
 #define MMP2_NR_CLKS                   200
 #endif
index ef61f36..60b94b9 100644 (file)
@@ -332,6 +332,8 @@ extern int bcma_arch_register_fallback_sprom(
                struct ssb_sprom *out));
 
 struct bcma_bus {
+       struct device *dev;
+
        /* The MMIO area. */
        void __iomem *mmio;
 
@@ -339,14 +341,7 @@ struct bcma_bus {
 
        enum bcma_hosttype hosttype;
        bool host_is_pcie2; /* Used for BCMA_HOSTTYPE_PCI only */
-       union {
-               /* Pointer to the PCI bus (only for BCMA_HOSTTYPE_PCI) */
-               struct pci_dev *host_pci;
-               /* Pointer to the SDIO device (only for BCMA_HOSTTYPE_SDIO) */
-               struct sdio_func *host_sdio;
-               /* Pointer to platform device (only for BCMA_HOSTTYPE_SOC) */
-               struct platform_device *host_pdev;
-       };
+       struct pci_dev *host_pci; /* PCI bus pointer (BCMA_HOSTTYPE_PCI only) */
 
        struct bcma_chipinfo chipinfo;
 
index 8804753..7bb2d8d 100644 (file)
@@ -116,7 +116,13 @@ extern void blk_fill_rwbs(char *rwbs, unsigned int op, int bytes);
 
 static inline sector_t blk_rq_trace_sector(struct request *rq)
 {
-       return blk_rq_is_passthrough(rq) ? 0 : blk_rq_pos(rq);
+       /*
+        * Tracing should ignore starting sector for passthrough requests and
+        * requests where starting sector didn't get set.
+        */
+       if (blk_rq_is_passthrough(rq) || blk_rq_pos(rq) == (sector_t)-1)
+               return 0;
+       return blk_rq_pos(rq);
 }
 
 static inline unsigned int blk_rq_trace_nr_sectors(struct request *rq)
index 588dd5f..695b2a8 100644 (file)
@@ -78,7 +78,7 @@ int cgroup_bpf_inherit(struct cgroup *cgrp);
 int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,
                        enum bpf_attach_type type, u32 flags);
 int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
-                       enum bpf_attach_type type, u32 flags);
+                       enum bpf_attach_type type);
 int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
                       union bpf_attr __user *uattr);
 
index 0394f1f..de18227 100644 (file)
@@ -72,14 +72,15 @@ struct bpf_map {
        u32 value_size;
        u32 max_entries;
        u32 map_flags;
-       u32 pages;
+       int spin_lock_off; /* >=0 valid offset, <0 error */
        u32 id;
        int numa_node;
        u32 btf_key_type_id;
        u32 btf_value_type_id;
        struct btf *btf;
+       u32 pages;
        bool unpriv_array;
-       /* 55 bytes hole */
+       /* 51 bytes hole */
 
        /* The 3rd and 4th cacheline with misc members to avoid false sharing
         * particularly with refcounting.
@@ -91,6 +92,36 @@ struct bpf_map {
        char name[BPF_OBJ_NAME_LEN];
 };
 
+static inline bool map_value_has_spin_lock(const struct bpf_map *map)
+{
+       return map->spin_lock_off >= 0;
+}
+
+static inline void check_and_init_map_lock(struct bpf_map *map, void *dst)
+{
+       if (likely(!map_value_has_spin_lock(map)))
+               return;
+       *(struct bpf_spin_lock *)(dst + map->spin_lock_off) =
+               (struct bpf_spin_lock){};
+}
+
+/* copy everything but bpf_spin_lock */
+static inline void copy_map_value(struct bpf_map *map, void *dst, void *src)
+{
+       if (unlikely(map_value_has_spin_lock(map))) {
+               u32 off = map->spin_lock_off;
+
+               memcpy(dst, src, off);
+               memcpy(dst + off + sizeof(struct bpf_spin_lock),
+                      src + off + sizeof(struct bpf_spin_lock),
+                      map->value_size - off - sizeof(struct bpf_spin_lock));
+       } else {
+               memcpy(dst, src, map->value_size);
+       }
+}
+void copy_map_value_locked(struct bpf_map *map, void *dst, void *src,
+                          bool lock_src);
+
 struct bpf_offload_dev;
 struct bpf_offloaded_map;
 
@@ -162,6 +193,8 @@ enum bpf_arg_type {
        ARG_PTR_TO_CTX,         /* pointer to context */
        ARG_ANYTHING,           /* any (initialized) argument is ok */
        ARG_PTR_TO_SOCKET,      /* pointer to bpf_sock */
+       ARG_PTR_TO_SPIN_LOCK,   /* pointer to bpf_spin_lock */
+       ARG_PTR_TO_SOCK_COMMON, /* pointer to sock_common */
 };
 
 /* type of values returned from helper functions */
@@ -171,6 +204,7 @@ enum bpf_return_type {
        RET_PTR_TO_MAP_VALUE,           /* returns a pointer to map elem value */
        RET_PTR_TO_MAP_VALUE_OR_NULL,   /* returns a pointer to map elem value or NULL */
        RET_PTR_TO_SOCKET_OR_NULL,      /* returns a pointer to a socket or NULL */
+       RET_PTR_TO_TCP_SOCK_OR_NULL,    /* returns a pointer to a tcp_sock or NULL */
 };
 
 /* eBPF function prototype used by verifier to allow BPF_CALLs from eBPF programs
@@ -224,6 +258,10 @@ enum bpf_reg_type {
        PTR_TO_FLOW_KEYS,        /* reg points to bpf_flow_keys */
        PTR_TO_SOCKET,           /* reg points to struct bpf_sock */
        PTR_TO_SOCKET_OR_NULL,   /* reg points to struct bpf_sock or NULL */
+       PTR_TO_SOCK_COMMON,      /* reg points to sock_common */
+       PTR_TO_SOCK_COMMON_OR_NULL, /* reg points to sock_common or NULL */
+       PTR_TO_TCP_SOCK,         /* reg points to struct tcp_sock */
+       PTR_TO_TCP_SOCK_OR_NULL, /* reg points to struct tcp_sock or NULL */
 };
 
 /* The information passed from prog-specific *_is_valid_access
@@ -735,8 +773,9 @@ int bpf_map_offload_get_next_key(struct bpf_map *map,
 bool bpf_offload_prog_map_match(struct bpf_prog *prog, struct bpf_map *map);
 
 struct bpf_offload_dev *
-bpf_offload_dev_create(const struct bpf_prog_offload_ops *ops);
+bpf_offload_dev_create(const struct bpf_prog_offload_ops *ops, void *priv);
 void bpf_offload_dev_destroy(struct bpf_offload_dev *offdev);
+void *bpf_offload_dev_priv(struct bpf_offload_dev *offdev);
 int bpf_offload_dev_netdev_register(struct bpf_offload_dev *offdev,
                                    struct net_device *netdev);
 void bpf_offload_dev_netdev_unregister(struct bpf_offload_dev *offdev,
@@ -879,7 +918,8 @@ extern const struct bpf_func_proto bpf_msg_redirect_hash_proto;
 extern const struct bpf_func_proto bpf_msg_redirect_map_proto;
 extern const struct bpf_func_proto bpf_sk_redirect_hash_proto;
 extern const struct bpf_func_proto bpf_sk_redirect_map_proto;
-
+extern const struct bpf_func_proto bpf_spin_lock_proto;
+extern const struct bpf_func_proto bpf_spin_unlock_proto;
 extern const struct bpf_func_proto bpf_get_local_storage_proto;
 
 /* Shared helpers among cBPF and eBPF. */
@@ -887,6 +927,9 @@ void bpf_user_rnd_init_once(void);
 u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
 
 #if defined(CONFIG_NET)
+bool bpf_sock_common_is_valid_access(int off, int size,
+                                    enum bpf_access_type type,
+                                    struct bpf_insn_access_aux *info);
 bool bpf_sock_is_valid_access(int off, int size, enum bpf_access_type type,
                              struct bpf_insn_access_aux *info);
 u32 bpf_sock_convert_ctx_access(enum bpf_access_type type,
@@ -895,6 +938,12 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type,
                                struct bpf_prog *prog,
                                u32 *target_size);
 #else
+static inline bool bpf_sock_common_is_valid_access(int off, int size,
+                                                  enum bpf_access_type type,
+                                                  struct bpf_insn_access_aux *info)
+{
+       return false;
+}
 static inline bool bpf_sock_is_valid_access(int off, int size,
                                            enum bpf_access_type type,
                                            struct bpf_insn_access_aux *info)
@@ -911,4 +960,31 @@ static inline u32 bpf_sock_convert_ctx_access(enum bpf_access_type type,
 }
 #endif
 
+#ifdef CONFIG_INET
+bool bpf_tcp_sock_is_valid_access(int off, int size, enum bpf_access_type type,
+                                 struct bpf_insn_access_aux *info);
+
+u32 bpf_tcp_sock_convert_ctx_access(enum bpf_access_type type,
+                                   const struct bpf_insn *si,
+                                   struct bpf_insn *insn_buf,
+                                   struct bpf_prog *prog,
+                                   u32 *target_size);
+#else
+static inline bool bpf_tcp_sock_is_valid_access(int off, int size,
+                                               enum bpf_access_type type,
+                                               struct bpf_insn_access_aux *info)
+{
+       return false;
+}
+
+static inline u32 bpf_tcp_sock_convert_ctx_access(enum bpf_access_type type,
+                                                 const struct bpf_insn *si,
+                                                 struct bpf_insn *insn_buf,
+                                                 struct bpf_prog *prog,
+                                                 u32 *target_size)
+{
+       return 0;
+}
+#endif /* CONFIG_INET */
+
 #endif /* _LINUX_BPF_H */
index 44d9ab4..08bf2f1 100644 (file)
@@ -6,9 +6,11 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_SOCKET_FILTER, sk_filter)
 BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_CLS, tc_cls_act)
 BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_ACT, tc_cls_act)
 BPF_PROG_TYPE(BPF_PROG_TYPE_XDP, xdp)
+#ifdef CONFIG_CGROUP_BPF
 BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SKB, cg_skb)
 BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK, cg_sock)
 BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, cg_sock_addr)
+#endif
 BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_IN, lwt_in)
 BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_OUT, lwt_out)
 BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_XMIT, lwt_xmit)
index 0620e41..69f7a34 100644 (file)
@@ -148,6 +148,7 @@ struct bpf_verifier_state {
        /* call stack tracking */
        struct bpf_func_state *frame[MAX_CALL_FRAMES];
        u32 curframe;
+       u32 active_spin_lock;
        bool speculative;
 };
 
index 12502e2..455d31b 100644 (file)
@@ -50,6 +50,7 @@ u32 btf_id(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);
+int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t);
 
 #ifdef CONFIG_BPF_SYSCALL
 const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id);
index 218df7f..5041357 100644 (file)
@@ -180,12 +180,10 @@ enum cpuhp_smt_control {
 #if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT)
 extern enum cpuhp_smt_control cpu_smt_control;
 extern void cpu_smt_disable(bool force);
-extern void cpu_smt_check_topology_early(void);
 extern void cpu_smt_check_topology(void);
 #else
 # define cpu_smt_control               (CPU_SMT_ENABLED)
 static inline void cpu_smt_disable(bool force) { }
-static inline void cpu_smt_check_topology_early(void) { }
 static inline void cpu_smt_check_topology(void) { }
 #endif
 
index ef4b70f..60996e6 100644 (file)
@@ -62,9 +62,10 @@ extern const struct qstr slash_name;
 struct dentry_stat_t {
        long nr_dentry;
        long nr_unused;
-       long age_limit;          /* age in seconds */
-       long want_pages;         /* pages requested by system */
-       long dummy[2];
+       long age_limit;         /* age in seconds */
+       long want_pages;        /* pages requested by system */
+       long nr_negative;       /* # of unused negative dentries */
+       long dummy;             /* Reserved for future use */
 };
 extern struct dentry_stat_t dentry_stat;
 
index afd9596..19a8de5 100644 (file)
@@ -400,4 +400,19 @@ struct ethtool_ops {
        void    (*get_ethtool_phy_stats)(struct net_device *,
                                         struct ethtool_stats *, u64 *);
 };
+
+struct ethtool_rx_flow_rule {
+       struct flow_rule        *rule;
+       unsigned long           priv[0];
+};
+
+struct ethtool_rx_flow_spec_input {
+       const struct ethtool_rx_flow_spec       *fs;
+       u32                                     rss_ctx;
+};
+
+struct ethtool_rx_flow_rule *
+ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input);
+void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *rule);
+
 #endif /* _LINUX_ETHTOOL_H */
index e4b473f..95e2d7e 100644 (file)
@@ -611,8 +611,8 @@ static inline u8 *bpf_skb_cb(struct sk_buff *skb)
        return qdisc_skb_cb(skb)->data;
 }
 
-static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog,
-                                      struct sk_buff *skb)
+static inline u32 __bpf_prog_run_save_cb(const struct bpf_prog *prog,
+                                        struct sk_buff *skb)
 {
        u8 *cb_data = bpf_skb_cb(skb);
        u8 cb_saved[BPF_SKB_CB_LEN];
@@ -631,15 +631,30 @@ static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog,
        return res;
 }
 
+static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog,
+                                      struct sk_buff *skb)
+{
+       u32 res;
+
+       preempt_disable();
+       res = __bpf_prog_run_save_cb(prog, skb);
+       preempt_enable();
+       return res;
+}
+
 static inline u32 bpf_prog_run_clear_cb(const struct bpf_prog *prog,
                                        struct sk_buff *skb)
 {
        u8 *cb_data = bpf_skb_cb(skb);
+       u32 res;
 
        if (unlikely(prog->cb_access))
                memset(cb_data, 0, BPF_SKB_CB_LEN);
 
-       return BPF_PROG_RUN(prog, skb);
+       preempt_disable();
+       res = BPF_PROG_RUN(prog, skb);
+       preempt_enable();
+       return res;
 }
 
 static __always_inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog,
@@ -880,7 +895,9 @@ bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
                     unsigned int alignment,
                     bpf_jit_fill_hole_t bpf_fill_ill_insns);
 void bpf_jit_binary_free(struct bpf_binary_header *hdr);
-
+u64 bpf_jit_alloc_exec_limit(void);
+void *bpf_jit_alloc_exec(unsigned long size);
+void bpf_jit_free_exec(void *addr);
 void bpf_jit_free(struct bpf_prog *fp);
 
 int bpf_jit_get_func_addr(const struct bpf_prog *prog,
index 811c777..29d8e2c 100644 (file)
@@ -1479,11 +1479,12 @@ struct super_block {
        struct user_namespace *s_user_ns;
 
        /*
-        * Keep the lru lists last in the structure so they always sit on their
-        * own individual cachelines.
+        * The list_lru structure is essentially just a pointer to a table
+        * of per-node lru lists, each of which has its own spinlock.
+        * There is no need to put them into separate cachelines.
         */
-       struct list_lru         s_dentry_lru ____cacheline_aligned_in_smp;
-       struct list_lru         s_inode_lru ____cacheline_aligned_in_smp;
+       struct list_lru         s_dentry_lru;
+       struct list_lru         s_inode_lru;
        struct rcu_head         rcu;
        struct work_struct      destroy_work;
 
index 94e9797..f127adb 100644 (file)
@@ -7,6 +7,7 @@
 #define __PTP_QORIQ_H__
 
 #include <linux/io.h>
+#include <linux/interrupt.h>
 #include <linux/ptp_clock_kernel.h>
 
 /*
@@ -49,7 +50,7 @@ struct etts_regs {
        u32 tmr_etts2_l;  /* Timestamp of general purpose external trigger */
 };
 
-struct qoriq_ptp_registers {
+struct ptp_qoriq_registers {
        struct ctrl_regs __iomem *ctrl_regs;
        struct alarm_regs __iomem *alarm_regs;
        struct fiper_regs __iomem *fiper_regs;
@@ -57,15 +58,15 @@ struct qoriq_ptp_registers {
 };
 
 /* Offset definitions for the four register groups */
-#define CTRL_REGS_OFFSET       0x0
-#define ALARM_REGS_OFFSET      0x40
-#define FIPER_REGS_OFFSET      0x80
-#define ETTS_REGS_OFFSET       0xa0
+#define ETSEC_CTRL_REGS_OFFSET 0x0
+#define ETSEC_ALARM_REGS_OFFSET        0x40
+#define ETSEC_FIPER_REGS_OFFSET        0x80
+#define ETSEC_ETTS_REGS_OFFSET 0xa0
 
-#define FMAN_CTRL_REGS_OFFSET  0x80
-#define FMAN_ALARM_REGS_OFFSET 0xb8
-#define FMAN_FIPER_REGS_OFFSET 0xd0
-#define FMAN_ETTS_REGS_OFFSET  0xe0
+#define CTRL_REGS_OFFSET       0x80
+#define ALARM_REGS_OFFSET      0xb8
+#define FIPER_REGS_OFFSET      0xd0
+#define ETTS_REGS_OFFSET       0xe0
 
 
 /* Bit definitions for the TMR_CTRL register */
@@ -136,9 +137,9 @@ struct qoriq_ptp_registers {
 #define DEFAULT_FIPER1_PERIOD  1000000000
 #define DEFAULT_FIPER2_PERIOD  100000
 
-struct qoriq_ptp {
+struct ptp_qoriq {
        void __iomem *base;
-       struct qoriq_ptp_registers regs;
+       struct ptp_qoriq_registers regs;
        spinlock_t lock; /* protects regs */
        struct ptp_clock *clock;
        struct ptp_clock_info caps;
@@ -156,28 +157,48 @@ struct qoriq_ptp {
        u32 cksel;
        u32 tmr_fiper1;
        u32 tmr_fiper2;
+       u32 (*read)(unsigned __iomem *addr);
+       void (*write)(unsigned __iomem *addr, u32 val);
 };
 
-static inline u32 qoriq_read(unsigned __iomem *addr)
+static inline u32 qoriq_read_be(unsigned __iomem *addr)
 {
-       u32 val;
-
-       val = ioread32be(addr);
-       return val;
+       return ioread32be(addr);
 }
 
-static inline void qoriq_write(unsigned __iomem *addr, u32 val)
+static inline void qoriq_write_be(unsigned __iomem *addr, u32 val)
 {
        iowrite32be(val, addr);
 }
 
+static inline u32 qoriq_read_le(unsigned __iomem *addr)
+{
+       return ioread32(addr);
+}
+
+static inline void qoriq_write_le(unsigned __iomem *addr, u32 val)
+{
+       iowrite32(val, addr);
+}
+
+irqreturn_t ptp_qoriq_isr(int irq, void *priv);
+int ptp_qoriq_init(struct ptp_qoriq *ptp_qoriq, void __iomem *base,
+                  const struct ptp_clock_info caps);
+void ptp_qoriq_free(struct ptp_qoriq *ptp_qoriq);
+int ptp_qoriq_adjfine(struct ptp_clock_info *ptp, long scaled_ppm);
+int ptp_qoriq_adjtime(struct ptp_clock_info *ptp, s64 delta);
+int ptp_qoriq_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts);
+int ptp_qoriq_settime(struct ptp_clock_info *ptp,
+                     const struct timespec64 *ts);
+int ptp_qoriq_enable(struct ptp_clock_info *ptp,
+                    struct ptp_clock_request *rq, int on);
 #ifdef CONFIG_DEBUG_FS
-void ptp_qoriq_create_debugfs(struct qoriq_ptp *qoriq_ptp);
-void ptp_qoriq_remove_debugfs(struct qoriq_ptp *qoriq_ptp);
+void ptp_qoriq_create_debugfs(struct ptp_qoriq *ptp_qoriq);
+void ptp_qoriq_remove_debugfs(struct ptp_qoriq *ptp_qoriq);
 #else
-static inline void ptp_qoriq_create_debugfs(struct qoriq_ptp *qoriq_ptp)
+static inline void ptp_qoriq_create_debugfs(struct ptp_qoriq *ptp_qoriq)
 { }
-static inline void ptp_qoriq_remove_debugfs(struct qoriq_ptp *qoriq_ptp)
+static inline void ptp_qoriq_remove_debugfs(struct ptp_qoriq *ptp_qoriq)
 { }
 #endif
 
index 8663f21..2d6100e 100644 (file)
 
 #ifdef CONFIG_DEBUG_FS
 
+#include <linux/kfifo.h>
+
 #define HID_DEBUG_BUFSIZE 512
+#define HID_DEBUG_FIFOSIZE 512
 
 void hid_dump_input(struct hid_device *, struct hid_usage *, __s32);
 void hid_dump_report(struct hid_device *, int , u8 *, int);
@@ -37,11 +40,8 @@ void hid_debug_init(void);
 void hid_debug_exit(void);
 void hid_debug_event(struct hid_device *, char *);
 
-
 struct hid_debug_list {
-       char *hid_debug_buf;
-       int head;
-       int tail;
+       DECLARE_KFIFO_PTR(hid_debug_fifo, char);
        struct fasync_struct *fasync;
        struct hid_device *hdev;
        struct list_head node;
@@ -64,4 +64,3 @@ struct hid_debug_list {
 #endif
 
 #endif
-
index e7d29ae..971cf76 100644 (file)
@@ -615,6 +615,7 @@ struct ide_drive_s {
 
        /* current sense rq and buffer */
        bool sense_rq_armed;
+       bool sense_rq_active;
        struct request *sense_rq;
        struct request_sense sense_data;
 
@@ -1219,6 +1220,7 @@ extern void ide_stall_queue(ide_drive_t *drive, unsigned long timeout);
 extern void ide_timer_expiry(struct timer_list *t);
 extern irqreturn_t ide_intr(int irq, void *dev_id);
 extern blk_status_t ide_queue_rq(struct blk_mq_hw_ctx *, const struct blk_mq_queue_data *);
+extern blk_status_t ide_issue_rq(ide_drive_t *, struct request *, bool);
 extern void ide_requeue_and_plug(ide_drive_t *drive, struct request *rq);
 
 void ide_init_disk(struct gendisk *, ide_drive_t *);
index 8b4348f..cc85f45 100644 (file)
@@ -137,7 +137,13 @@ extern void ip_mc_up(struct in_device *);
 extern void ip_mc_down(struct in_device *);
 extern void ip_mc_unmap(struct in_device *);
 extern void ip_mc_remap(struct in_device *);
-extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr);
+extern void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp);
+static inline void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
+{
+       return __ip_mc_dec_group(in_dev, addr, GFP_KERNEL);
+}
+extern void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
+                             gfp_t gfp);
 extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr);
 int ip_mc_check_igmp(struct sk_buff *skb);
 
index c672f34..4a728db 100644 (file)
@@ -260,6 +260,7 @@ struct irq_affinity {
 /**
  * struct irq_affinity_desc - Interrupt affinity descriptor
  * @mask:      cpumask to hold the affinity assignment
+ * @is_managed: 1 if the interrupt is managed internally
  */
 struct irq_affinity_desc {
        struct cpumask  mask;
index 071b4cb..c848a7c 100644 (file)
 #define GITS_TYPER_PLPIS               (1UL << 0)
 #define GITS_TYPER_VLPIS               (1UL << 1)
 #define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT        4
-#define GITS_TYPER_ITT_ENTRY_SIZE(r)   ((((r) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) & 0x1f) + 1)
+#define GITS_TYPER_ITT_ENTRY_SIZE(r)   ((((r) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) & 0xf) + 1)
 #define GITS_TYPER_IDBITS_SHIFT                8
 #define GITS_TYPER_DEVBITS_SHIFT       13
 #define GITS_TYPER_DEVBITS(r)          ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
index bfa7114..dd46828 100644 (file)
@@ -261,6 +261,31 @@ static inline u16 ethtool_adv_to_mmd_eee_adv_t(u32 adv)
        return reg;
 }
 
+/**
+ * linkmode_adv_to_mii_10gbt_adv_t
+ * @advertising: the linkmode advertisement settings
+ *
+ * A small helper function that translates linkmode advertisement
+ * settings to phy autonegotiation advertisements for the C45
+ * 10GBASE-T AN CONTROL (7.32) register.
+ */
+static inline u32 linkmode_adv_to_mii_10gbt_adv_t(unsigned long *advertising)
+{
+       u32 result = 0;
+
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+                             advertising))
+               result |= MDIO_AN_10GBT_CTRL_ADV2_5G;
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+                             advertising))
+               result |= MDIO_AN_10GBT_CTRL_ADV5G;
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+                             advertising))
+               result |= MDIO_AN_10GBT_CTRL_ADV10G;
+
+       return result;
+}
+
 int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
 int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
 
index 07da5c6..368267c 100644 (file)
@@ -21,14 +21,16 @@ struct vmem_altmap;
  * walkers which rely on the fully initialized page->flags and others
  * should use this rather than pfn_valid && pfn_to_page
  */
-#define pfn_to_online_page(pfn)                                \
-({                                                     \
-       struct page *___page = NULL;                    \
-       unsigned long ___nr = pfn_to_section_nr(pfn);   \
-                                                       \
-       if (___nr < NR_MEM_SECTIONS && online_section_nr(___nr))\
-               ___page = pfn_to_page(pfn);             \
-       ___page;                                        \
+#define pfn_to_online_page(pfn)                                           \
+({                                                                \
+       struct page *___page = NULL;                               \
+       unsigned long ___pfn = pfn;                                \
+       unsigned long ___nr = pfn_to_section_nr(___pfn);           \
+                                                                  \
+       if (___nr < NR_MEM_SECTIONS && online_section_nr(___nr) && \
+           pfn_valid_within(___pfn))                              \
+               ___page = pfn_to_page(___pfn);                     \
+       ___page;                                                   \
 })
 
 /*
index 8c4a820..f93a559 100644 (file)
@@ -67,7 +67,7 @@
 #define MLX5_UN_SZ_BYTES(typ) (sizeof(union mlx5_ifc_##typ##_bits) / 8)
 #define MLX5_UN_SZ_DW(typ) (sizeof(union mlx5_ifc_##typ##_bits) / 32)
 #define MLX5_BYTE_OFF(typ, fld) (__mlx5_bit_off(typ, fld) / 8)
-#define MLX5_ADDR_OF(typ, p, fld) ((char *)(p) + MLX5_BYTE_OFF(typ, fld))
+#define MLX5_ADDR_OF(typ, p, fld) ((void *)((uint8_t *)(p) + MLX5_BYTE_OFF(typ, fld)))
 
 /* insert a value to a struct */
 #define MLX5_SET(typ, p, fld, v) do { \
@@ -342,6 +342,8 @@ enum mlx5_event {
        MLX5_EVENT_TYPE_PAGE_FAULT         = 0xc,
        MLX5_EVENT_TYPE_NIC_VPORT_CHANGE   = 0xd,
 
+       MLX5_EVENT_TYPE_HOST_PARAMS_CHANGE = 0xe,
+
        MLX5_EVENT_TYPE_DCT_DRAINED        = 0x1c,
 
        MLX5_EVENT_TYPE_FPGA_ERROR         = 0x20,
@@ -591,7 +593,7 @@ struct mlx5_eqe_cmd {
 };
 
 struct mlx5_eqe_page_req {
-       u8              rsvd0[2];
+       __be16          ec_function;
        __be16          func_id;
        __be32          num_pages;
        __be32          rsvd1[5];
@@ -1201,6 +1203,9 @@ enum mlx5_qcam_feature_groups {
 #define MLX5_CAP_ODP(mdev, cap)\
        MLX5_GET(odp_cap, mdev->caps.hca_cur[MLX5_CAP_ODP], cap)
 
+#define MLX5_CAP_ODP_MAX(mdev, cap)\
+       MLX5_GET(odp_cap, mdev->caps.hca_max[MLX5_CAP_ODP], cap)
+
 #define MLX5_CAP_VECTOR_CALC(mdev, cap) \
        MLX5_GET(vector_calc_cap, \
                 mdev->caps.hca_cur[MLX5_CAP_VECTOR_CALC], cap)
index 5429925..c2de50f 100644 (file)
@@ -522,6 +522,7 @@ struct mlx5_priv {
        atomic_t                reg_pages;
        struct list_head        free_list;
        int                     vfs_pages;
+       int                     peer_pf_pages;
 
        struct mlx5_core_health health;
 
@@ -652,6 +653,7 @@ struct mlx5_core_dev {
                u32 mcam[MLX5_ST_SZ_DW(mcam_reg)];
                u32 fpga[MLX5_ST_SZ_DW(fpga_cap)];
                u32 qcam[MLX5_ST_SZ_DW(qcam_reg)];
+               u8  embedded_cpu;
        } caps;
        u64                     sys_image_guid;
        phys_addr_t             iseg_base;
@@ -850,11 +852,30 @@ void mlx5_cmd_cleanup(struct mlx5_core_dev *dev);
 void mlx5_cmd_use_events(struct mlx5_core_dev *dev);
 void mlx5_cmd_use_polling(struct mlx5_core_dev *dev);
 
+struct mlx5_async_ctx {
+       struct mlx5_core_dev *dev;
+       atomic_t num_inflight;
+       struct wait_queue_head wait;
+};
+
+struct mlx5_async_work;
+
+typedef void (*mlx5_async_cbk_t)(int status, struct mlx5_async_work *context);
+
+struct mlx5_async_work {
+       struct mlx5_async_ctx *ctx;
+       mlx5_async_cbk_t user_callback;
+};
+
+void mlx5_cmd_init_async_ctx(struct mlx5_core_dev *dev,
+                            struct mlx5_async_ctx *ctx);
+void mlx5_cmd_cleanup_async_ctx(struct mlx5_async_ctx *ctx);
+int mlx5_cmd_exec_cb(struct mlx5_async_ctx *ctx, void *in, int in_size,
+                    void *out, int out_size, mlx5_async_cbk_t callback,
+                    struct mlx5_async_work *work);
+
 int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
                  int out_size);
-int mlx5_cmd_exec_cb(struct mlx5_core_dev *dev, void *in, int in_size,
-                    void *out, int out_size, mlx5_cmd_cbk_t callback,
-                    void *context);
 int mlx5_cmd_exec_polling(struct mlx5_core_dev *dev, void *in, int in_size,
                          void *out, int out_size);
 void mlx5_cmd_mbox_status(void *out, u8 *status, u32 *syndrome);
@@ -885,9 +906,10 @@ void mlx5_init_mkey_table(struct mlx5_core_dev *dev);
 void mlx5_cleanup_mkey_table(struct mlx5_core_dev *dev);
 int mlx5_core_create_mkey_cb(struct mlx5_core_dev *dev,
                             struct mlx5_core_mkey *mkey,
-                            u32 *in, int inlen,
-                            u32 *out, int outlen,
-                            mlx5_cmd_cbk_t callback, void *context);
+                            struct mlx5_async_ctx *async_ctx, u32 *in,
+                            int inlen, u32 *out, int outlen,
+                            mlx5_async_cbk_t callback,
+                            struct mlx5_async_work *context);
 int mlx5_core_create_mkey(struct mlx5_core_dev *dev,
                          struct mlx5_core_mkey *mkey,
                          u32 *in, int inlen);
@@ -897,14 +919,12 @@ int mlx5_core_query_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mkey *mkey,
                         u32 *out, int outlen);
 int mlx5_core_alloc_pd(struct mlx5_core_dev *dev, u32 *pdn);
 int mlx5_core_dealloc_pd(struct mlx5_core_dev *dev, u32 pdn);
-int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, const void *inb, void *outb,
-                     u16 opmod, u8 port);
 int mlx5_pagealloc_init(struct mlx5_core_dev *dev);
 void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev);
 void mlx5_pagealloc_start(struct mlx5_core_dev *dev);
 void mlx5_pagealloc_stop(struct mlx5_core_dev *dev);
 void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id,
-                                s32 npages);
+                                s32 npages, bool ec_function);
 int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot);
 int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev);
 void mlx5_register_debugfs(void);
@@ -1058,11 +1078,29 @@ static inline int mlx5_core_is_pf(struct mlx5_core_dev *dev)
        return !(dev->priv.pci_dev_data & MLX5_PCI_DEV_IS_VF);
 }
 
-#define MLX5_TOTAL_VPORTS(mdev) (1 + pci_sriov_get_totalvfs((mdev)->pdev))
-#define MLX5_VPORT_MANAGER(mdev) \
-       (MLX5_CAP_GEN(mdev, vport_group_manager) && \
-        (MLX5_CAP_GEN(mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH) && \
-        mlx5_core_is_pf(mdev))
+static inline bool mlx5_core_is_ecpf(struct mlx5_core_dev *dev)
+{
+       return dev->caps.embedded_cpu;
+}
+
+static inline bool mlx5_core_is_ecpf_esw_manager(struct mlx5_core_dev *dev)
+{
+       return dev->caps.embedded_cpu && MLX5_CAP_GEN(dev, eswitch_manager);
+}
+
+static inline bool mlx5_ecpf_vport_exists(struct mlx5_core_dev *dev)
+{
+       return mlx5_core_is_pf(dev) && MLX5_CAP_ESW(dev, ecpf_vport_exists);
+}
+
+#define MLX5_HOST_PF_MAX_VFS   (127u)
+static inline u16 mlx5_core_max_vfs(struct mlx5_core_dev *dev)
+{
+       if (mlx5_core_is_ecpf_esw_manager(dev))
+               return MLX5_HOST_PF_MAX_VFS;
+       else
+               return pci_sriov_get_totalvfs(dev->pdev);
+}
 
 static inline int mlx5_get_gid_table_len(u16 param)
 {
index fab5121..96d8435 100644 (file)
@@ -22,6 +22,12 @@ enum {
        NUM_REP_TYPES,
 };
 
+enum {
+       REP_UNREGISTERED,
+       REP_REGISTERED,
+       REP_LOADED,
+};
+
 struct mlx5_eswitch_rep;
 struct mlx5_eswitch_rep_if {
        int                    (*load)(struct mlx5_core_dev *dev,
@@ -29,7 +35,7 @@ struct mlx5_eswitch_rep_if {
        void                   (*unload)(struct mlx5_eswitch_rep *rep);
        void                   *(*get_proto_dev)(struct mlx5_eswitch_rep *rep);
        void                    *priv;
-       bool                   valid;
+       u8                      state;
 };
 
 struct mlx5_eswitch_rep {
@@ -40,13 +46,10 @@ struct mlx5_eswitch_rep {
        u32                    vlan_refcount;
 };
 
-void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw,
-                                    int vport_index,
-                                    struct mlx5_eswitch_rep_if *rep_if,
-                                    u8 rep_type);
-void mlx5_eswitch_unregister_vport_rep(struct mlx5_eswitch *esw,
-                                      int vport_index,
-                                      u8 rep_type);
+void mlx5_eswitch_register_vport_reps(struct mlx5_eswitch *esw,
+                                     struct mlx5_eswitch_rep_if *rep_if,
+                                     u8 rep_type);
+void mlx5_eswitch_unregister_vport_reps(struct mlx5_eswitch *esw, u8 rep_type);
 void *mlx5_eswitch_get_proto_dev(struct mlx5_eswitch *esw,
                                 int vport,
                                 u8 rep_type);
index 35fe521..b7bb774 100644 (file)
@@ -72,6 +72,7 @@ enum {
 
 enum {
        MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE        = 0x0,
+       MLX5_SET_HCA_CAP_OP_MOD_ODP                   = 0x2,
        MLX5_SET_HCA_CAP_OP_MOD_ATOMIC                = 0x3,
 };
 
@@ -141,6 +142,7 @@ enum {
        MLX5_CMD_OP_QUERY_XRQ_DC_PARAMS_ENTRY     = 0x725,
        MLX5_CMD_OP_SET_XRQ_DC_PARAMS_ENTRY       = 0x726,
        MLX5_CMD_OP_QUERY_XRQ_ERROR_PARAMS        = 0x727,
+       MLX5_CMD_OP_QUERY_HOST_PARAMS             = 0x740,
        MLX5_CMD_OP_QUERY_VPORT_STATE             = 0x750,
        MLX5_CMD_OP_MODIFY_VPORT_STATE            = 0x751,
        MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT       = 0x752,
@@ -629,7 +631,8 @@ struct mlx5_ifc_e_switch_cap_bits {
        u8         vport_svlan_insert[0x1];
        u8         vport_cvlan_insert_if_not_exist[0x1];
        u8         vport_cvlan_insert_overwrite[0x1];
-       u8         reserved_at_5[0x17];
+       u8         reserved_at_5[0x16];
+       u8         ecpf_vport_exists[0x1];
        u8         counter_eswitch_affinity[0x1];
        u8         merged_eswitch[0x1];
        u8         nic_vport_node_guid_modify[0x1];
@@ -831,7 +834,9 @@ struct mlx5_ifc_odp_cap_bits {
 
        struct mlx5_ifc_odp_per_transport_service_cap_bits ud_odp_caps;
 
-       u8         reserved_at_e0[0x720];
+       struct mlx5_ifc_odp_per_transport_service_cap_bits xrc_odp_caps;
+
+       u8         reserved_at_100[0x700];
 };
 
 struct mlx5_ifc_calc_op {
@@ -4438,7 +4443,8 @@ struct mlx5_ifc_query_pages_out_bits {
 
        u8         syndrome[0x20];
 
-       u8         reserved_at_40[0x10];
+       u8         embedded_cpu_function[0x1];
+       u8         reserved_at_41[0xf];
        u8         function_id[0x10];
 
        u8         num_pages[0x20];
@@ -4457,7 +4463,8 @@ struct mlx5_ifc_query_pages_in_bits {
        u8         reserved_at_20[0x10];
        u8         op_mod[0x10];
 
-       u8         reserved_at_40[0x10];
+       u8         embedded_cpu_function[0x1];
+       u8         reserved_at_41[0xf];
        u8         function_id[0x10];
 
        u8         reserved_at_60[0x20];
@@ -5877,7 +5884,8 @@ struct mlx5_ifc_manage_pages_in_bits {
        u8         reserved_at_20[0x10];
        u8         op_mod[0x10];
 
-       u8         reserved_at_40[0x10];
+       u8         embedded_cpu_function[0x1];
+       u8         reserved_at_41[0xf];
        u8         function_id[0x10];
 
        u8         input_num_entries[0x20];
@@ -6055,7 +6063,8 @@ struct mlx5_ifc_enable_hca_in_bits {
        u8         reserved_at_20[0x10];
        u8         op_mod[0x10];
 
-       u8         reserved_at_40[0x10];
+       u8         embedded_cpu_function[0x1];
+       u8         reserved_at_41[0xf];
        u8         function_id[0x10];
 
        u8         reserved_at_60[0x20];
@@ -6099,7 +6108,8 @@ struct mlx5_ifc_disable_hca_in_bits {
        u8         reserved_at_20[0x10];
        u8         op_mod[0x10];
 
-       u8         reserved_at_40[0x10];
+       u8         embedded_cpu_function[0x1];
+       u8         reserved_at_41[0xf];
        u8         function_id[0x10];
 
        u8         reserved_at_60[0x20];
@@ -7817,21 +7827,23 @@ struct mlx5_ifc_ptys_reg_bits {
        u8         proto_mask[0x3];
 
        u8         an_status[0x4];
-       u8         reserved_at_24[0x3c];
+       u8         reserved_at_24[0x1c];
+
+       u8         ext_eth_proto_capability[0x20];
 
        u8         eth_proto_capability[0x20];
 
        u8         ib_link_width_capability[0x10];
        u8         ib_proto_capability[0x10];
 
-       u8         reserved_at_a0[0x20];
+       u8         ext_eth_proto_admin[0x20];
 
        u8         eth_proto_admin[0x20];
 
        u8         ib_link_width_admin[0x10];
        u8         ib_proto_admin[0x10];
 
-       u8         reserved_at_100[0x20];
+       u8         ext_eth_proto_oper[0x20];
 
        u8         eth_proto_oper[0x20];
 
@@ -8280,7 +8292,9 @@ struct mlx5_ifc_mpegc_reg_bits {
 struct mlx5_ifc_pcam_enhanced_features_bits {
        u8         reserved_at_0[0x6d];
        u8         rx_icrc_encapsulated_counter[0x1];
-       u8         reserved_at_6e[0x8];
+       u8         reserved_at_6e[0x4];
+       u8         ptys_extended_ethernet[0x1];
+       u8         reserved_at_73[0x3];
        u8         pfcc_mask[0x1];
        u8         reserved_at_77[0x3];
        u8         per_lane_error_counters[0x1];
@@ -8746,7 +8760,8 @@ struct mlx5_ifc_initial_seg_bits {
        u8         initializing[0x1];
        u8         reserved_at_fe1[0x4];
        u8         nic_interface_supported[0x3];
-       u8         reserved_at_fe8[0x18];
+       u8         embedded_cpu[0x1];
+       u8         reserved_at_fe9[0x17];
 
        struct mlx5_ifc_health_buffer_bits health_buffer;
 
@@ -9513,4 +9528,44 @@ struct mlx5_ifc_mtrc_ctrl_bits {
        u8         reserved_at_80[0x180];
 };
 
+struct mlx5_ifc_host_params_context_bits {
+       u8         host_number[0x8];
+       u8         reserved_at_8[0x8];
+       u8         host_num_of_vfs[0x10];
+
+       u8         reserved_at_20[0x10];
+       u8         host_pci_bus[0x10];
+
+       u8         reserved_at_40[0x10];
+       u8         host_pci_device[0x10];
+
+       u8         reserved_at_60[0x10];
+       u8         host_pci_function[0x10];
+
+       u8         reserved_at_80[0x180];
+};
+
+struct mlx5_ifc_query_host_params_in_bits {
+       u8         opcode[0x10];
+       u8         reserved_at_10[0x10];
+
+       u8         reserved_at_20[0x10];
+       u8         op_mod[0x10];
+
+       u8         reserved_at_40[0x40];
+};
+
+struct mlx5_ifc_query_host_params_out_bits {
+       u8         status[0x8];
+       u8         reserved_at_8[0x18];
+
+       u8         syndrome[0x20];
+
+       u8         reserved_at_40[0x40];
+
+       struct mlx5_ifc_host_params_context_bits host_params_context;
+
+       u8         reserved_at_280[0x180];
+};
+
 #endif /* MLX5_IFC_H */
index bf4bc01..814fa19 100644 (file)
@@ -92,6 +92,22 @@ enum mlx5e_link_mode {
        MLX5E_LINK_MODES_NUMBER,
 };
 
+enum mlx5e_ext_link_mode {
+       MLX5E_SGMII_100M                        = 0,
+       MLX5E_1000BASE_X_SGMII                  = 1,
+       MLX5E_5GBASE_R                          = 3,
+       MLX5E_10GBASE_XFI_XAUI_1                = 4,
+       MLX5E_40GBASE_XLAUI_4_XLPPI_4           = 5,
+       MLX5E_25GAUI_1_25GBASE_CR_KR            = 6,
+       MLX5E_50GAUI_2_LAUI_2_50GBASE_CR2_KR2   = 7,
+       MLX5E_50GAUI_1_LAUI_1_50GBASE_CR_KR     = 8,
+       MLX5E_CAUI_4_100GBASE_CR4_KR4           = 9,
+       MLX5E_100GAUI_2_100GBASE_CR2_KR2        = 10,
+       MLX5E_200GAUI_4_200GBASE_CR4_KR4        = 12,
+       MLX5E_400GAUI_8                         = 15,
+       MLX5E_EXT_LINK_MODES_NUMBER,
+};
+
 enum mlx5e_connector_type {
        MLX5E_PORT_UNKNOWN      = 0,
        MLX5E_PORT_NONE                 = 1,
@@ -106,31 +122,23 @@ enum mlx5e_connector_type {
 };
 
 #define MLX5E_PROT_MASK(link_mode) (1 << link_mode)
+#define MLX5_GET_ETH_PROTO(reg, out, ext, field)       \
+       (ext ? MLX5_GET(reg, out, ext_##field) :        \
+       MLX5_GET(reg, out, field))
 
 int mlx5_set_port_caps(struct mlx5_core_dev *dev, u8 port_num, u32 caps);
 int mlx5_query_port_ptys(struct mlx5_core_dev *dev, u32 *ptys,
                         int ptys_size, int proto_mask, u8 local_port);
-int mlx5_query_port_proto_cap(struct mlx5_core_dev *dev,
-                             u32 *proto_cap, int proto_mask);
-int mlx5_query_port_proto_admin(struct mlx5_core_dev *dev,
-                               u32 *proto_admin, int proto_mask);
 int mlx5_query_port_link_width_oper(struct mlx5_core_dev *dev,
                                    u8 *link_width_oper, u8 local_port);
 int mlx5_query_port_ib_proto_oper(struct mlx5_core_dev *dev,
                                  u8 *proto_oper, u8 local_port);
-int mlx5_query_port_eth_proto_oper(struct mlx5_core_dev *dev,
-                                  u32 *proto_oper, u8 local_port);
-int mlx5_set_port_ptys(struct mlx5_core_dev *dev, bool an_disable,
-                      u32 proto_admin, int proto_mask);
 void mlx5_toggle_port_link(struct mlx5_core_dev *dev);
 int mlx5_set_port_admin_status(struct mlx5_core_dev *dev,
                               enum mlx5_port_status status);
 int mlx5_query_port_admin_status(struct mlx5_core_dev *dev,
                                 enum mlx5_port_status *status);
 int mlx5_set_port_beacon(struct mlx5_core_dev *dev, u16 beacon_duration);
-void mlx5_query_port_autoneg(struct mlx5_core_dev *dev, int proto_mask,
-                            u8 *an_status,
-                            u8 *an_disable_cap, u8 *an_disable_admin);
 
 int mlx5_set_port_mtu(struct mlx5_core_dev *dev, u16 mtu, u8 port);
 void mlx5_query_port_max_mtu(struct mlx5_core_dev *dev, u16 *max_mtu, u8 port);
index 9c69480..0eef548 100644 (file)
 #include <linux/mlx5/driver.h>
 #include <linux/mlx5/device.h>
 
+#define MLX5_VPORT_PF_PLACEHOLDER              (1u)
+#define MLX5_VPORT_UPLINK_PLACEHOLDER          (1u)
+#define MLX5_VPORT_ECPF_PLACEHOLDER(mdev)      (mlx5_ecpf_vport_exists(mdev))
+
+#define MLX5_SPECIAL_VPORTS(mdev) (MLX5_VPORT_PF_PLACEHOLDER +         \
+                                  MLX5_VPORT_UPLINK_PLACEHOLDER +      \
+                                  MLX5_VPORT_ECPF_PLACEHOLDER(mdev))
+
+#define MLX5_TOTAL_VPORTS(mdev)        (MLX5_SPECIAL_VPORTS(mdev) +            \
+                                mlx5_core_max_vfs(mdev))
+
+#define MLX5_VPORT_MANAGER(mdev)                                       \
+       (MLX5_CAP_GEN(mdev, vport_group_manager) &&                     \
+        (MLX5_CAP_GEN(mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH) &&   \
+        mlx5_core_is_pf(mdev))
+
 enum {
        MLX5_CAP_INLINE_MODE_L2,
        MLX5_CAP_INLINE_MODE_VPORT_CONTEXT,
        MLX5_CAP_INLINE_MODE_NOT_REQUIRED,
 };
 
+enum {
+       MLX5_VPORT_PF                   = 0x0,
+       MLX5_VPORT_FIRST_VF             = 0x1,
+       MLX5_VPORT_ECPF                 = 0xfffe,
+       MLX5_VPORT_UPLINK               = 0xffff
+};
+
 u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport);
 int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
-                                 u16 vport, u8 state);
+                                 u16 vport, u8 other_vport, u8 state);
 int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev,
                                     u16 vport, u8 *addr);
 int mlx5_query_nic_vport_min_inline(struct mlx5_core_dev *mdev,
@@ -60,7 +83,7 @@ int mlx5_query_nic_vport_system_image_guid(struct mlx5_core_dev *mdev,
                                           u64 *system_image_guid);
 int mlx5_query_nic_vport_node_guid(struct mlx5_core_dev *mdev, u64 *node_guid);
 int mlx5_modify_nic_vport_node_guid(struct mlx5_core_dev *mdev,
-                                   u32 vport, u64 node_guid);
+                                   u16 vport, u64 node_guid);
 int mlx5_query_nic_vport_qkey_viol_cntr(struct mlx5_core_dev *mdev,
                                        u16 *qkey_viol_cntr);
 int mlx5_query_hca_vport_gid(struct mlx5_core_dev *dev, u8 other_vport,
@@ -78,7 +101,7 @@ int mlx5_query_hca_vport_system_image_guid(struct mlx5_core_dev *dev,
 int mlx5_query_hca_vport_node_guid(struct mlx5_core_dev *dev,
                                   u64 *node_guid);
 int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
-                                 u32 vport,
+                                 u16 vport,
                                  enum mlx5_list_type list_type,
                                  u8 addr_list[][ETH_ALEN],
                                  int *list_size);
@@ -87,7 +110,7 @@ int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev,
                                   u8 addr_list[][ETH_ALEN],
                                   int list_size);
 int mlx5_query_nic_vport_promisc(struct mlx5_core_dev *mdev,
-                                u32 vport,
+                                u16 vport,
                                 int *promisc_uc,
                                 int *promisc_mc,
                                 int *promisc_all);
@@ -96,7 +119,7 @@ int mlx5_modify_nic_vport_promisc(struct mlx5_core_dev *mdev,
                                  int promisc_mc,
                                  int promisc_all);
 int mlx5_query_nic_vport_vlans(struct mlx5_core_dev *dev,
-                              u32 vport,
+                              u16 vport,
                               u16 vlans[],
                               int *size);
 int mlx5_modify_nic_vport_vlans(struct mlx5_core_dev *dev,
@@ -106,7 +129,7 @@ int mlx5_modify_nic_vport_vlans(struct mlx5_core_dev *dev,
 int mlx5_nic_vport_enable_roce(struct mlx5_core_dev *mdev);
 int mlx5_nic_vport_disable_roce(struct mlx5_core_dev *mdev);
 int mlx5_query_vport_down_stats(struct mlx5_core_dev *mdev, u16 vport,
-                               u64 *rx_discard_vport_down,
+                               u8 other_vport, u64 *rx_discard_vport_down,
                                u64 *tx_discard_vport_down);
 int mlx5_core_query_vport_counter(struct mlx5_core_dev *dev, u8 other_vport,
                                  int vf, u8 port_num, void *out,
index 2c471a2..0a36a22 100644 (file)
@@ -95,6 +95,13 @@ struct page {
                         */
                        unsigned long private;
                };
+               struct {        /* page_pool used by netstack */
+                       /**
+                        * @dma_addr: might require a 64-bit value even on
+                        * 32-bit architectures.
+                        */
+                       dma_addr_t dma_addr;
+               };
                struct {        /* slab, slob and slub */
                        union {
                                struct list_head slab_list;     /* uses lru */
index de73778..8ef3300 100644 (file)
@@ -308,6 +308,7 @@ struct mmc_card {
        unsigned int    nr_parts;
 
        unsigned int            bouncesz;       /* Bounce buffer size */
+       struct workqueue_struct *complete_wq;   /* Private workqueue */
 };
 
 static inline bool mmc_large_sector(struct mmc_card *card)
index e675ef9..aab4d9f 100644 (file)
@@ -868,7 +868,6 @@ enum bpf_netdev_command {
        /* BPF program for offload callbacks, invoked at program load time. */
        BPF_OFFLOAD_MAP_ALLOC,
        BPF_OFFLOAD_MAP_FREE,
-       XDP_QUERY_XSK_UMEM,
        XDP_SETUP_XSK_UMEM,
 };
 
@@ -895,10 +894,10 @@ struct netdev_bpf {
                struct {
                        struct bpf_offloaded_map *offmap;
                };
-               /* XDP_QUERY_XSK_UMEM, XDP_SETUP_XSK_UMEM */
+               /* XDP_SETUP_XSK_UMEM */
                struct {
-                       struct xdp_umem *umem; /* out for query*/
-                       u16 queue_id; /* in for query */
+                       struct xdp_umem *umem;
+                       u16 queue_id;
                } xsk;
        };
 };
@@ -1188,6 +1187,10 @@ struct dev_ifalias {
  *     not implement this, it is assumed that the hw is not able to have
  *     multiple net devices on single physical port.
  *
+ * int (*ndo_get_port_parent_id)(struct net_device *dev,
+ *                              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
@@ -1412,6 +1415,8 @@ struct net_device_ops {
                                                      bool new_carrier);
        int                     (*ndo_get_phys_port_id)(struct net_device *dev,
                                                        struct netdev_phys_item_id *ppid);
+       int                     (*ndo_get_port_parent_id)(struct net_device *dev,
+                                                         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,
@@ -1486,6 +1491,7 @@ struct net_device_ops {
  * @IFF_NO_RX_HANDLER: device doesn't support the rx_handler hook
  * @IFF_FAILOVER: device is a failover master device
  * @IFF_FAILOVER_SLAVE: device is lower dev of a failover master device
+ * @IFF_L3MDEV_RX_HANDLER: only invoke the rx handler of L3 master device
  */
 enum netdev_priv_flags {
        IFF_802_1Q_VLAN                 = 1<<0,
@@ -1517,6 +1523,7 @@ enum netdev_priv_flags {
        IFF_NO_RX_HANDLER               = 1<<26,
        IFF_FAILOVER                    = 1<<27,
        IFF_FAILOVER_SLAVE              = 1<<28,
+       IFF_L3MDEV_RX_HANDLER           = 1<<29,
 };
 
 #define IFF_802_1Q_VLAN                        IFF_802_1Q_VLAN
@@ -1547,6 +1554,7 @@ enum netdev_priv_flags {
 #define IFF_NO_RX_HANDLER              IFF_NO_RX_HANDLER
 #define IFF_FAILOVER                   IFF_FAILOVER
 #define IFF_FAILOVER_SLAVE             IFF_FAILOVER_SLAVE
+#define IFF_L3MDEV_RX_HANDLER          IFF_L3MDEV_RX_HANDLER
 
 /**
  *     struct net_device - The DEVICE structure.
@@ -3651,6 +3659,9 @@ int dev_get_phys_port_id(struct net_device *dev,
                         struct netdev_phys_item_id *ppid);
 int dev_get_phys_port_name(struct net_device *dev,
                           char *name, size_t len);
+int dev_get_port_parent_id(struct net_device *dev,
+                          struct netdev_phys_item_id *ppid, bool recurse);
+bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b);
 int dev_change_proto_down(struct net_device *dev, bool proto_down);
 struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again);
 struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
@@ -4552,6 +4563,11 @@ static inline bool netif_supports_nofcs(struct net_device *dev)
        return dev->priv_flags & IFF_SUPP_NOFCS;
 }
 
+static inline bool netif_has_l3_rx_handler(const struct net_device *dev)
+{
+       return dev->priv_flags & IFF_L3MDEV_RX_HANDLER;
+}
+
 static inline bool netif_is_l3_master(const struct net_device *dev)
 {
        return dev->priv_flags & IFF_L3MDEV_MASTER;
@@ -4663,22 +4679,22 @@ static inline const char *netdev_reg_state(const struct net_device *dev)
        return " (unknown)";
 }
 
-__printf(3, 4)
+__printf(3, 4) __cold
 void netdev_printk(const char *level, const struct net_device *dev,
                   const char *format, ...);
-__printf(2, 3)
+__printf(2, 3) __cold
 void netdev_emerg(const struct net_device *dev, const char *format, ...);
-__printf(2, 3)
+__printf(2, 3) __cold
 void netdev_alert(const struct net_device *dev, const char *format, ...);
-__printf(2, 3)
+__printf(2, 3) __cold
 void netdev_crit(const struct net_device *dev, const char *format, ...);
-__printf(2, 3)
+__printf(2, 3) __cold
 void netdev_err(const struct net_device *dev, const char *format, ...);
-__printf(2, 3)
+__printf(2, 3) __cold
 void netdev_warn(const struct net_device *dev, const char *format, ...);
-__printf(2, 3)
+__printf(2, 3) __cold
 void netdev_notice(const struct net_device *dev, const char *format, ...);
-__printf(2, 3)
+__printf(2, 3) __cold
 void netdev_info(const struct net_device *dev, const char *format, ...);
 
 #define netdev_level_once(level, dev, fmt, ...)                        \
index 34f38c1..7802177 100644 (file)
@@ -6,14 +6,19 @@
 
 struct objagg_ops {
        size_t obj_size;
+       bool (*delta_check)(void *priv, const void *parent_obj,
+                           const void *obj);
+       int (*hints_obj_cmp)(const void *obj1, const void *obj2);
        void * (*delta_create)(void *priv, void *parent_obj, void *obj);
        void (*delta_destroy)(void *priv, void *delta_priv);
-       void * (*root_create)(void *priv, void *obj);
+       void * (*root_create)(void *priv, void *obj, unsigned int root_id);
+#define OBJAGG_OBJ_ROOT_ID_INVALID UINT_MAX
        void (*root_destroy)(void *priv, void *root_priv);
 };
 
 struct objagg;
 struct objagg_obj;
+struct objagg_hints;
 
 const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj);
 const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj);
@@ -21,7 +26,8 @@ const void *objagg_obj_raw(const struct objagg_obj *objagg_obj);
 
 struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj);
 void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj);
-struct objagg *objagg_create(const struct objagg_ops *ops, void *priv);
+struct objagg *objagg_create(const struct objagg_ops *ops,
+                            struct objagg_hints *hints, void *priv);
 void objagg_destroy(struct objagg *objagg);
 
 struct objagg_obj_stats {
@@ -36,6 +42,7 @@ struct objagg_obj_stats_info {
 };
 
 struct objagg_stats {
+       unsigned int root_count;
        unsigned int stats_info_count;
        struct objagg_obj_stats_info stats_info[];
 };
@@ -43,4 +50,14 @@ struct objagg_stats {
 const struct objagg_stats *objagg_stats_get(struct objagg *objagg);
 void objagg_stats_put(const struct objagg_stats *objagg_stats);
 
+enum objagg_opt_algo_type {
+       OBJAGG_OPT_ALGO_SIMPLE_GREEDY,
+};
+
+struct objagg_hints *objagg_hints_get(struct objagg *objagg,
+                                     enum objagg_opt_algo_type opt_algo_type);
+void objagg_hints_put(struct objagg_hints *objagg_hints);
+const struct objagg_stats *
+objagg_hints_stats_get(struct objagg_hints *objagg_hints);
+
 #endif
index 70f83d0..3db507e 100644 (file)
@@ -502,6 +502,12 @@ struct phy_driver {
         */
        int (*probe)(struct phy_device *phydev);
 
+       /*
+        * Probe the hardware to determine what abilities it has.
+        * Should only set phydev->supported.
+        */
+       int (*get_features)(struct phy_device *phydev);
+
        /* PHY Power Management */
        int (*suspend)(struct phy_device *phydev);
        int (*resume)(struct phy_device *phydev);
@@ -667,13 +673,8 @@ phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
                   bool exact);
 size_t phy_speeds(unsigned int *speeds, size_t size,
                  unsigned long *mask);
-
-static inline bool __phy_is_started(struct phy_device *phydev)
-{
-       WARN_ON(!mutex_is_locked(&phydev->lock));
-
-       return phydev->state >= PHY_UP;
-}
+void of_set_phy_supported(struct phy_device *phydev);
+void of_set_phy_eee_broken(struct phy_device *phydev);
 
 /**
  * phy_is_started - Convenience function to check whether PHY is started
@@ -681,29 +682,12 @@ static inline bool __phy_is_started(struct phy_device *phydev)
  */
 static inline bool phy_is_started(struct phy_device *phydev)
 {
-       bool started;
-
-       mutex_lock(&phydev->lock);
-       started = __phy_is_started(phydev);
-       mutex_unlock(&phydev->lock);
-
-       return started;
+       return phydev->state >= PHY_UP;
 }
 
 void phy_resolve_aneg_linkmode(struct phy_device *phydev);
 
 /**
- * phy_read_mmd - Convenience function for reading a register
- * from an MMD on a given PHY.
- * @phydev: The phy_device struct
- * @devad: The MMD to read from
- * @regnum: The register on the MMD to read
- *
- * Same rules as for phy_read();
- */
-int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum);
-
-/**
  * phy_read - Convenience function for reading a given PHY register
  * @phydev: the phy_device struct
  * @regnum: register number to read
@@ -758,9 +742,68 @@ static inline int __phy_write(struct phy_device *phydev, u32 regnum, u16 val)
                               val);
 }
 
+/**
+ * phy_read_mmd - Convenience function for reading a register
+ * from an MMD on a given PHY.
+ * @phydev: The phy_device struct
+ * @devad: The MMD to read from
+ * @regnum: The register on the MMD to read
+ *
+ * Same rules as for phy_read();
+ */
+int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum);
+
+/**
+ * __phy_read_mmd - Convenience function for reading a register
+ * from an MMD on a given PHY.
+ * @phydev: The phy_device struct
+ * @devad: The MMD to read from
+ * @regnum: The register on the MMD to read
+ *
+ * Same rules as for __phy_read();
+ */
+int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum);
+
+/**
+ * phy_write_mmd - Convenience function for writing a register
+ * on an MMD on a given PHY.
+ * @phydev: The phy_device struct
+ * @devad: The MMD to write to
+ * @regnum: The register on the MMD to read
+ * @val: value to write to @regnum
+ *
+ * Same rules as for phy_write();
+ */
+int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val);
+
+/**
+ * __phy_write_mmd - Convenience function for writing a register
+ * on an MMD on a given PHY.
+ * @phydev: The phy_device struct
+ * @devad: The MMD to write to
+ * @regnum: The register on the MMD to read
+ * @val: value to write to @regnum
+ *
+ * Same rules as for __phy_write();
+ */
+int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val);
+
+int __phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask,
+                        u16 set);
+int phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask,
+                      u16 set);
 int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set);
 int phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set);
 
+int __phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
+                            u16 mask, u16 set);
+int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
+                          u16 mask, u16 set);
+int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
+                    u16 mask, u16 set);
+int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
+                  u16 mask, u16 set);
+
 /**
  * __phy_set_bits - Convenience function for setting bits in a PHY register
  * @phydev: the phy_device struct
@@ -811,6 +854,66 @@ static inline int phy_clear_bits(struct phy_device *phydev, u32 regnum, u16 val)
 }
 
 /**
+ * __phy_set_bits_mmd - Convenience function for setting bits in a register
+ * on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @val: bits to set
+ *
+ * The caller must have taken the MDIO bus lock.
+ */
+static inline int __phy_set_bits_mmd(struct phy_device *phydev, int devad,
+               u32 regnum, u16 val)
+{
+       return __phy_modify_mmd(phydev, devad, regnum, 0, val);
+}
+
+/**
+ * __phy_clear_bits_mmd - Convenience function for clearing bits in a register
+ * on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @val: bits to clear
+ *
+ * The caller must have taken the MDIO bus lock.
+ */
+static inline int __phy_clear_bits_mmd(struct phy_device *phydev, int devad,
+               u32 regnum, u16 val)
+{
+       return __phy_modify_mmd(phydev, devad, regnum, val, 0);
+}
+
+/**
+ * phy_set_bits_mmd - Convenience function for setting bits in a register
+ * on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @val: bits to set
+ */
+static inline int phy_set_bits_mmd(struct phy_device *phydev, int devad,
+               u32 regnum, u16 val)
+{
+       return phy_modify_mmd(phydev, devad, regnum, 0, val);
+}
+
+/**
+ * phy_clear_bits_mmd - Convenience function for clearing bits in a register
+ * on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @val: bits to clear
+ */
+static inline int phy_clear_bits_mmd(struct phy_device *phydev, int devad,
+               u32 regnum, u16 val)
+{
+       return phy_modify_mmd(phydev, devad, regnum, val, 0);
+}
+
+/**
  * phy_interrupt_is_valid - Convenience function for testing a given PHY irq
  * @phydev: the phy_device struct
  *
@@ -886,18 +989,6 @@ static inline bool phy_is_pseudo_fixed_link(struct phy_device *phydev)
        return phydev->is_pseudo_fixed_link;
 }
 
-/**
- * phy_write_mmd - Convenience function for writing a register
- * on an MMD on a given PHY.
- * @phydev: The phy_device struct
- * @devad: The MMD to read from
- * @regnum: The register on the MMD to read
- * @val: value to write to @regnum
- *
- * Same rules as for phy_write();
- */
-int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val);
-
 int phy_save_page(struct phy_device *phydev);
 int phy_select_page(struct phy_device *phydev, int page);
 int phy_restore_page(struct phy_device *phydev, int oldpage, int ret);
@@ -1006,12 +1097,14 @@ int genphy_write_mmd_unsupported(struct phy_device *phdev, int devnum,
 /* Clause 45 PHY */
 int genphy_c45_restart_aneg(struct phy_device *phydev);
 int genphy_c45_aneg_done(struct phy_device *phydev);
-int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask);
+int genphy_c45_read_link(struct phy_device *phydev);
 int genphy_c45_read_lpa(struct phy_device *phydev);
 int genphy_c45_read_pma(struct phy_device *phydev);
 int genphy_c45_pma_setup_forced(struct phy_device *phydev);
+int genphy_c45_an_config_aneg(struct phy_device *phydev);
 int genphy_c45_an_disable_aneg(struct phy_device *phydev);
 int genphy_c45_read_mdix(struct phy_device *phydev);
+int genphy_c45_pma_read_abilities(struct phy_device *phydev);
 
 /* The gen10g_* functions are the old Clause 45 stub */
 int gen10g_config_aneg(struct phy_device *phydev);
index 9525567..1e5d86e 100644 (file)
@@ -15,30 +15,41 @@ struct device_node;
 #if IS_ENABLED(CONFIG_FIXED_PHY)
 extern int fixed_phy_change_carrier(struct net_device *dev, bool new_carrier);
 extern int fixed_phy_add(unsigned int irq, int phy_id,
-                        struct fixed_phy_status *status,
-                        int link_gpio);
+                        struct fixed_phy_status *status);
 extern struct phy_device *fixed_phy_register(unsigned int irq,
                                             struct fixed_phy_status *status,
-                                            int link_gpio,
                                             struct device_node *np);
+
+extern struct phy_device *
+fixed_phy_register_with_gpiod(unsigned int irq,
+                             struct fixed_phy_status *status,
+                             struct gpio_desc *gpiod);
+
 extern void fixed_phy_unregister(struct phy_device *phydev);
 extern int fixed_phy_set_link_update(struct phy_device *phydev,
                        int (*link_update)(struct net_device *,
                                           struct fixed_phy_status *));
 #else
 static inline int fixed_phy_add(unsigned int irq, int phy_id,
-                               struct fixed_phy_status *status,
-                               int link_gpio)
+                               struct fixed_phy_status *status)
 {
        return -ENODEV;
 }
 static inline struct phy_device *fixed_phy_register(unsigned int irq,
                                                struct fixed_phy_status *status,
-                                               int gpio_link,
                                                struct device_node *np)
 {
        return ERR_PTR(-ENODEV);
 }
+
+static inline struct phy_device *
+fixed_phy_register_with_gpiod(unsigned int irq,
+                             struct fixed_phy_status *status,
+                             struct gpio_desc *gpiod)
+{
+       return ERR_PTR(-ENODEV);
+}
+
 static inline void fixed_phy_unregister(struct phy_device *phydev)
 {
 }
index 021fc65..f57059e 100644 (file)
@@ -220,6 +220,7 @@ void phylink_ethtool_get_pauseparam(struct phylink *,
 int phylink_ethtool_set_pauseparam(struct phylink *,
                                   struct ethtool_pauseparam *);
 int phylink_get_eee_err(struct phylink *);
+int phylink_init_eee(struct phylink *, bool);
 int phylink_ethtool_get_eee(struct phylink *, struct ethtool_eee *);
 int phylink_ethtool_set_eee(struct phylink *, struct ethtool_eee *);
 int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
index 54af4ee..fed5be7 100644 (file)
@@ -105,7 +105,7 @@ static inline bool pm_runtime_callbacks_present(struct device *dev)
 
 static inline void pm_runtime_mark_last_busy(struct device *dev)
 {
-       WRITE_ONCE(dev->power.last_busy, ktime_to_ns(ktime_get()));
+       WRITE_ONCE(dev->power.last_busy, ktime_get_mono_fast_ns());
 }
 
 static inline bool pm_runtime_is_irq_safe(struct device *dev)
index 186cd8e..8da46ac 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/cache.h>
 #include <linux/types.h>
 #include <linux/compiler.h>
-#include <linux/cache.h>
 #include <linux/slab.h>
 #include <asm/errno.h>
 #endif
index 35170f7..f6165d3 100644 (file)
@@ -643,6 +643,7 @@ struct qed_dev_info {
        u16             mtu;
 
        bool wol_support;
+       bool smart_an;
 
        /* MBI version */
        u32 mbi_version;
index d2f90fa..bba3afb 100644 (file)
@@ -995,7 +995,7 @@ struct task_struct {
        /* cg_list protected by css_set_lock and tsk->alloc_lock: */
        struct list_head                cg_list;
 #endif
-#ifdef CONFIG_X86_RESCTRL
+#ifdef CONFIG_X86_CPU_RESCTRL
        u32                             closid;
        u32                             rmid;
 #endif
index ec912d0..ecdc654 100644 (file)
@@ -71,6 +71,7 @@ static inline int get_dumpable(struct mm_struct *mm)
 #define MMF_HUGE_ZERO_PAGE     23      /* mm has ever used the global huge zero page */
 #define MMF_DISABLE_THP                24      /* disable THP for all VMAs */
 #define MMF_OOM_VICTIM         25      /* mm is the oom victim */
+#define MMF_OOM_REAP_QUEUED    26      /* mm was queued for oom_reaper */
 #define MMF_DISABLE_THP_MASK   (1 << MMF_DISABLE_THP)
 
 #define MMF_INIT_MASK          (MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK |\
index 10b19a1..545f371 100644 (file)
  * called near the end of a function. Otherwise, the list can be
  * re-initialized for later re-use by wake_q_init().
  *
- * Note that this can cause spurious wakeups. schedule() callers
+ * NOTE that this can cause spurious wakeups. schedule() callers
  * must ensure the call is done inside a loop, confirming that the
  * wakeup condition has in fact occurred.
+ *
+ * NOTE that there is no guarantee the wakeup will happen any later than the
+ * wake_q_add() location. Therefore task must be ready to be woken at the
+ * location of the wake_q_add().
  */
 
 #include <linux/sched.h>
index cc7e2c1..9702016 100644 (file)
@@ -392,7 +392,7 @@ extern bool unhandled_signal(struct task_struct *tsk, int sig);
 #endif
 
 #define siginmask(sig, mask) \
-       ((sig) < SIGRTMIN && (rt_sigmask(sig) & (mask)))
+       ((sig) > 0 && (sig) < SIGRTMIN && (rt_sigmask(sig) & (mask)))
 
 #define SIG_KERNEL_ONLY_MASK (\
        rt_sigmask(SIGKILL)   |  rt_sigmask(SIGSTOP))
index c345953..a41e84f 100644 (file)
@@ -1889,12 +1889,12 @@ static inline void __skb_queue_before(struct sk_buff_head *list,
  *
  *     A buffer cannot be placed on two lists at the same time.
  */
-void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk);
 static inline void __skb_queue_head(struct sk_buff_head *list,
                                    struct sk_buff *newsk)
 {
        __skb_queue_after(list, (struct sk_buff *)list, newsk);
 }
+void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk);
 
 /**
  *     __skb_queue_tail - queue a buffer at the list tail
@@ -1906,12 +1906,12 @@ static inline void __skb_queue_head(struct sk_buff_head *list,
  *
  *     A buffer cannot be placed on two lists at the same time.
  */
-void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk);
 static inline void __skb_queue_tail(struct sk_buff_head *list,
                                   struct sk_buff *newsk)
 {
        __skb_queue_before(list, (struct sk_buff *)list, newsk);
 }
+void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk);
 
 /*
  * remove sk_buff from list. _Must_ be called atomically, and with
@@ -1938,7 +1938,6 @@ static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
  *     so must be used with appropriate locks held only. The head item is
  *     returned or %NULL if the list is empty.
  */
-struct sk_buff *skb_dequeue(struct sk_buff_head *list);
 static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
 {
        struct sk_buff *skb = skb_peek(list);
@@ -1946,6 +1945,7 @@ static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
                __skb_unlink(skb, list);
        return skb;
 }
+struct sk_buff *skb_dequeue(struct sk_buff_head *list);
 
 /**
  *     __skb_dequeue_tail - remove from the tail of the queue
@@ -1955,7 +1955,6 @@ static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
  *     so must be used with appropriate locks held only. The tail item is
  *     returned or %NULL if the list is empty.
  */
-struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list);
 static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list)
 {
        struct sk_buff *skb = skb_peek_tail(list);
@@ -1963,6 +1962,7 @@ static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list)
                __skb_unlink(skb, list);
        return skb;
 }
+struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list);
 
 
 static inline bool skb_is_nonlinear(const struct sk_buff *skb)
@@ -2653,13 +2653,13 @@ static inline int skb_orphan_frags_rx(struct sk_buff *skb, gfp_t gfp_mask)
  *     the list and one reference dropped. This function does not take the
  *     list lock and the caller must hold the relevant locks to use it.
  */
-void skb_queue_purge(struct sk_buff_head *list);
 static inline void __skb_queue_purge(struct sk_buff_head *list)
 {
        struct sk_buff *skb;
        while ((skb = __skb_dequeue(list)) != NULL)
                kfree_skb(skb);
 }
+void skb_queue_purge(struct sk_buff_head *list);
 
 unsigned int skb_rbtree_purge(struct rb_root *root);
 
@@ -3028,7 +3028,7 @@ static inline int skb_padto(struct sk_buff *skb, unsigned int len)
 }
 
 /**
- *     skb_put_padto - increase size and pad an skbuff up to a minimal size
+ *     __skb_put_padto - increase size and pad an skbuff up to a minimal size
  *     @skb: buffer to pad
  *     @len: minimal length
  *     @free_on_error: free buffer on error
@@ -3486,16 +3486,25 @@ static inline ktime_t skb_get_ktime(const struct sk_buff *skb)
 /**
  *     skb_get_timestamp - get timestamp from a skb
  *     @skb: skb to get stamp from
- *     @stamp: pointer to struct timeval to store stamp in
+ *     @stamp: pointer to struct __kernel_old_timeval to store stamp in
  *
  *     Timestamps are stored in the skb as offsets to a base timestamp.
  *     This function converts the offset back to a struct timeval and stores
  *     it in stamp.
  */
 static inline void skb_get_timestamp(const struct sk_buff *skb,
-                                    struct timeval *stamp)
+                                    struct __kernel_old_timeval *stamp)
 {
-       *stamp = ktime_to_timeval(skb->tstamp);
+       *stamp = ns_to_kernel_old_timeval(skb->tstamp);
+}
+
+static inline void skb_get_new_timestamp(const struct sk_buff *skb,
+                                        struct __kernel_sock_timeval *stamp)
+{
+       struct timespec64 ts = ktime_to_timespec64(skb->tstamp);
+
+       stamp->tv_sec = ts.tv_sec;
+       stamp->tv_usec = ts.tv_nsec / 1000;
 }
 
 static inline void skb_get_timestampns(const struct sk_buff *skb,
@@ -3504,6 +3513,15 @@ static inline void skb_get_timestampns(const struct sk_buff *skb,
        *stamp = ktime_to_timespec(skb->tstamp);
 }
 
+static inline void skb_get_new_timestampns(const struct sk_buff *skb,
+                                          struct __kernel_timespec *stamp)
+{
+       struct timespec64 ts = ktime_to_timespec64(skb->tstamp);
+
+       stamp->tv_sec = ts.tv_sec;
+       stamp->tv_nsec = ts.tv_nsec;
+}
+
 static inline void __net_timestamp(struct sk_buff *skb)
 {
        skb->tstamp = ktime_get_real();
index ab2041a..6016dae 100644 (file)
@@ -349,9 +349,17 @@ struct ucred {
 extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr);
 extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
 
+struct timespec64;
 struct __kernel_timespec;
 struct old_timespec32;
 
+struct scm_timestamping_internal {
+       struct timespec64 ts[3];
+};
+
+extern void put_cmsg_scm_timestamping64(struct msghdr *msg, struct scm_timestamping_internal *tss);
+extern void put_cmsg_scm_timestamping(struct msghdr *msg, struct scm_timestamping_internal *tss);
+
 /* The __sys_...msg variants allow MSG_CMSG_COMPAT iff
  * forbid_cmsg_compat==false
  */
index 7ddfc65..4335bd7 100644 (file)
@@ -184,6 +184,7 @@ struct plat_stmmacenet_data {
        struct clk *pclk;
        struct clk *clk_ptp_ref;
        unsigned int clk_ptp_rate;
+       unsigned int clk_ref_rate;
        struct reset_control *stmmac_rst;
        struct stmmac_axi *axi;
        int has_gmac4;
index dbc795e..c745e9c 100644 (file)
@@ -80,7 +80,7 @@ static inline void tcf_tm_dump(struct tcf_t *dtm, const struct tcf_t *stm)
 struct tc_action_ops {
        struct list_head head;
        char    kind[IFNAMSIZ];
-       __u32   type; /* TBD to match kind */
+       enum tca_id  id; /* identifier should match kind */
        size_t  size;
        struct module           *owner;
        int     (*act)(struct sk_buff *, const struct tc_action *,
index 20d523e..269ec27 100644 (file)
@@ -248,6 +248,7 @@ struct ipv6_stub {
                                 const struct in6_addr *addr);
        int (*ipv6_dst_lookup)(struct net *net, struct sock *sk,
                               struct dst_entry **dst, struct flowi6 *fl6);
+       int (*ipv6_route_input)(struct sk_buff *skb);
 
        struct fib6_table *(*fib6_get_table)(struct net *net, u32 id);
        struct fib6_info *(*fib6_lookup)(struct net *net, int oif,
index e0c41eb..7f2739a 100644 (file)
@@ -836,6 +836,17 @@ struct cfg80211_bitrate_mask {
 };
 
 /**
+ * enum cfg80211_ap_settings_flags - AP settings flags
+ *
+ * Used by cfg80211_ap_settings
+ *
+ * @AP_SETTINGS_EXTERNAL_AUTH_SUPPORT: AP supports external authentication
+ */
+enum cfg80211_ap_settings_flags {
+       AP_SETTINGS_EXTERNAL_AUTH_SUPPORT = BIT(0),
+};
+
+/**
  * struct cfg80211_ap_settings - AP configuration
  *
  * Used to configure an AP interface.
@@ -865,6 +876,7 @@ struct cfg80211_bitrate_mask {
  * @he_cap: HE capabilities (or %NULL if HE isn't enabled)
  * @ht_required: stations must support HT
  * @vht_required: stations must support VHT
+ * @flags: flags, as defined in enum cfg80211_ap_settings_flags
  */
 struct cfg80211_ap_settings {
        struct cfg80211_chan_def chandef;
@@ -890,6 +902,7 @@ struct cfg80211_ap_settings {
        const struct ieee80211_vht_cap *vht_cap;
        const struct ieee80211_he_cap_elem *he_cap;
        bool ht_required, vht_required;
+       u32 flags;
 };
 
 /**
@@ -1003,6 +1016,7 @@ enum station_parameters_apply_mask {
  * @support_p2p_ps: information if station supports P2P PS mechanism
  * @he_capa: HE capabilities of station
  * @he_capa_len: the length of the HE capabilities
+ * @airtime_weight: airtime scheduler weight for this station
  */
 struct station_parameters {
        const u8 *supported_rates;
@@ -1032,6 +1046,7 @@ struct station_parameters {
        int support_p2p_ps;
        const struct ieee80211_he_cap_elem *he_capa;
        u8 he_capa_len;
+       u16 airtime_weight;
 };
 
 /**
@@ -1300,6 +1315,8 @@ struct cfg80211_tid_stats {
  *     from this peer
  * @connected_to_gate: true if mesh STA has a path to mesh gate
  * @rx_duration: aggregate PPDU duration(usecs) for all the frames from a peer
+ * @tx_duration: aggregate PPDU duration(usecs) for all the frames to a peer
+ * @airtime_weight: current airtime scheduling weight
  * @pertid: per-TID statistics, see &struct cfg80211_tid_stats, using the last
  *     (IEEE80211_NUM_TIDS) index for MSDUs not encapsulated in QoS-MPDUs.
  *     Note that this doesn't use the @filled bit, but is used if non-NULL.
@@ -1350,8 +1367,9 @@ struct station_info {
 
        u32 expected_throughput;
 
-       u64 rx_beacon;
+       u64 tx_duration;
        u64 rx_duration;
+       u64 rx_beacon;
        u8 rx_beacon_signal_avg;
        u8 connected_to_gate;
 
@@ -1359,6 +1377,8 @@ struct station_info {
        s8 ack_signal;
        s8 avg_ack_signal;
 
+       u16 airtime_weight;
+
        u32 rx_mpdu_count;
        u32 fcs_err_count;
 };
@@ -1422,6 +1442,8 @@ enum monitor_flags {
  * @MPATH_INFO_DISCOVERY_TIMEOUT: @discovery_timeout filled
  * @MPATH_INFO_DISCOVERY_RETRIES: @discovery_retries filled
  * @MPATH_INFO_FLAGS: @flags filled
+ * @MPATH_INFO_HOP_COUNT: @hop_count filled
+ * @MPATH_INFO_PATH_CHANGE: @path_change_count filled
  */
 enum mpath_info_flags {
        MPATH_INFO_FRAME_QLEN           = BIT(0),
@@ -1431,6 +1453,8 @@ enum mpath_info_flags {
        MPATH_INFO_DISCOVERY_TIMEOUT    = BIT(4),
        MPATH_INFO_DISCOVERY_RETRIES    = BIT(5),
        MPATH_INFO_FLAGS                = BIT(6),
+       MPATH_INFO_HOP_COUNT            = BIT(7),
+       MPATH_INFO_PATH_CHANGE          = BIT(8),
 };
 
 /**
@@ -1450,6 +1474,8 @@ enum mpath_info_flags {
  *     This number should increase every time the list of mesh paths
  *     changes, i.e. when a station is added or removed, so that
  *     userspace can tell whether it got a consistent snapshot.
+ * @hop_count: hops to destination
+ * @path_change_count: total number of path changes to destination
  */
 struct mpath_info {
        u32 filled;
@@ -1460,6 +1486,8 @@ struct mpath_info {
        u32 discovery_timeout;
        u8 discovery_retries;
        u8 flags;
+       u8 hop_count;
+       u32 path_change_count;
 
        int generation;
 };
@@ -2391,6 +2419,8 @@ enum wiphy_params_flags {
        WIPHY_PARAM_TXQ_QUANTUM         = 1 << 8,
 };
 
+#define IEEE80211_DEFAULT_AIRTIME_WEIGHT       256
+
 /**
  * struct cfg80211_pmksa - PMK Security Association
  *
@@ -2815,6 +2845,7 @@ struct cfg80211_pmk_conf {
  *     use %WLAN_STATUS_UNSPECIFIED_FAILURE if user space cannot give you
  *     the real status code for failures. Used only for the authentication
  *     response command interface (user space to driver).
+ * @pmkid: The identifier to refer a PMKSA.
  */
 struct cfg80211_external_auth_params {
        enum nl80211_external_auth_action action;
@@ -2822,6 +2853,7 @@ struct cfg80211_external_auth_params {
        struct cfg80211_ssid ssid;
        unsigned int key_mgmt_suite;
        u16 status;
+       const u8 *pmkid;
 };
 
 /**
@@ -4112,6 +4144,8 @@ struct cfg80211_pmsr_capabilities {
  * @signal_type: signal type reported in &struct cfg80211_bss.
  * @cipher_suites: supported cipher suites
  * @n_cipher_suites: number of supported cipher suites
+ * @akm_suites: supported AKM suites
+ * @n_akm_suites: number of supported AKM suites
  * @retry_short: Retry limit for short frames (dot11ShortRetryLimit)
  * @retry_long: Retry limit for long frames (dot11LongRetryLimit)
  * @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold);
@@ -4310,6 +4344,9 @@ struct wiphy {
        int n_cipher_suites;
        const u32 *cipher_suites;
 
+       int n_akm_suites;
+       const u32 *akm_suites;
+
        u8 retry_short;
        u8 retry_long;
        u32 frag_threshold;
@@ -4573,6 +4610,17 @@ struct cfg80211_cqm_config;
  * @mesh_id_len: (private) Used by the internal configuration code
  * @mesh_id_up_len: (private) Used by the internal configuration code
  * @wext: (private) Used by the internal wireless extensions compat code
+ * @wext.ibss: (private) IBSS data part of wext handling
+ * @wext.connect: (private) connection handling data
+ * @wext.keys: (private) (WEP) key data
+ * @wext.ie: (private) extra elements for association
+ * @wext.ie_len: (private) length of extra elements
+ * @wext.bssid: (private) selected network BSSID
+ * @wext.ssid: (private) selected network SSID
+ * @wext.default_key: (private) selected default key index
+ * @wext.default_mgmt_key: (private) selected default management key index
+ * @wext.prev_bssid: (private) previous BSSID for reassociation
+ * @wext.prev_bssid_valid: (private) previous BSSID validity
  * @use_4addr: indicates 4addr mode is used on this interface, must be
  *     set by driver (if supported) on add_interface BEFORE registering the
  *     netdev and may otherwise be used by driver read-only, will be update
@@ -4672,7 +4720,8 @@ struct wireless_dev {
                struct cfg80211_cached_keys *keys;
                const u8 *ie;
                size_t ie_len;
-               u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
+               u8 bssid[ETH_ALEN];
+               u8 prev_bssid[ETH_ALEN];
                u8 ssid[IEEE80211_MAX_SSID_LEN];
                s8 default_key, default_mgmt_key;
                bool prev_bssid_valid;
@@ -5568,7 +5617,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
  * @dev: network device
  * @macaddr: the MAC address of the new candidate
  * @ie: information elements advertised by the peer candidate
- * @ie_len: lenght of the information elements buffer
+ * @ie_len: length of the information elements buffer
  * @gfp: allocation flags
  *
  * This function notifies cfg80211 that the mesh peer candidate has been
index 67f4293..a2da49d 100644 (file)
@@ -30,6 +30,7 @@ struct devlink {
        struct list_head param_list;
        struct list_head region_list;
        u32 snapshot_id;
+       struct list_head reporter_list;
        struct devlink_dpipe_headers *dpipe_headers;
        const struct devlink_ops *ops;
        struct device *dev;
@@ -48,6 +49,7 @@ struct devlink_port_attrs {
 
 struct devlink_port {
        struct list_head list;
+       struct list_head param_list;
        struct devlink *devlink;
        unsigned index;
        bool registered;
@@ -61,6 +63,7 @@ struct devlink_sb_pool_info {
        enum devlink_sb_pool_type pool_type;
        u32 size;
        enum devlink_sb_threshold_type threshold_type;
+       u32 cell_size;
 };
 
 /**
@@ -355,6 +358,7 @@ struct devlink_param_item {
        const struct devlink_param *param;
        union devlink_param_value driverinit_value;
        bool driverinit_value_valid;
+       bool published;
 };
 
 enum devlink_param_generic_id {
@@ -419,10 +423,50 @@ enum devlink_param_generic_id {
        .validate = _validate,                                          \
 }
 
+/* Part number, identifier of board design */
+#define DEVLINK_INFO_VERSION_GENERIC_BOARD_ID  "board.id"
+/* Revision of board design */
+#define DEVLINK_INFO_VERSION_GENERIC_BOARD_REV "board.rev"
+/* Maker of the board */
+#define DEVLINK_INFO_VERSION_GENERIC_BOARD_MANUFACTURE "board.manufacture"
+
+/* Control processor FW version */
+#define DEVLINK_INFO_VERSION_GENERIC_FW_MGMT   "fw.mgmt"
+/* Data path microcode controlling high-speed packet processing */
+#define DEVLINK_INFO_VERSION_GENERIC_FW_APP    "fw.app"
+/* UNDI software version */
+#define DEVLINK_INFO_VERSION_GENERIC_FW_UNDI   "fw.undi"
+/* NCSI support/handler version */
+#define DEVLINK_INFO_VERSION_GENERIC_FW_NCSI   "fw.ncsi"
+
 struct devlink_region;
+struct devlink_info_req;
 
 typedef void devlink_snapshot_data_dest_t(const void *data);
 
+struct devlink_fmsg;
+struct devlink_health_reporter;
+
+/**
+ * struct devlink_health_reporter_ops - Reporter operations
+ * @name: reporter name
+ * @recover: callback to recover from reported error
+ *           if priv_ctx is NULL, run a full recover
+ * @dump: callback to dump an object
+ *        if priv_ctx is NULL, run a full dump
+ * @diagnose: callback to diagnose the current status
+ */
+
+struct devlink_health_reporter_ops {
+       char *name;
+       int (*recover)(struct devlink_health_reporter *reporter,
+                      void *priv_ctx);
+       int (*dump)(struct devlink_health_reporter *reporter,
+                   struct devlink_fmsg *fmsg, void *priv_ctx);
+       int (*diagnose)(struct devlink_health_reporter *reporter,
+                       struct devlink_fmsg *fmsg);
+};
+
 struct devlink_ops {
        int (*reload)(struct devlink *devlink, struct netlink_ext_ack *extack);
        int (*port_type_set)(struct devlink_port *devlink_port,
@@ -475,6 +519,11 @@ struct devlink_ops {
        int (*eswitch_encap_mode_get)(struct devlink *devlink, u8 *p_encap_mode);
        int (*eswitch_encap_mode_set)(struct devlink *devlink, u8 encap_mode,
                                      struct netlink_ext_ack *extack);
+       int (*info_get)(struct devlink *devlink, struct devlink_info_req *req,
+                       struct netlink_ext_ack *extack);
+       int (*flash_update)(struct devlink *devlink, const char *file_name,
+                           const char *component,
+                           struct netlink_ext_ack *extack);
 };
 
 static inline void *devlink_priv(struct devlink *devlink)
@@ -567,11 +616,28 @@ int devlink_params_register(struct devlink *devlink,
 void devlink_params_unregister(struct devlink *devlink,
                               const struct devlink_param *params,
                               size_t params_count);
+void devlink_params_publish(struct devlink *devlink);
+void devlink_params_unpublish(struct devlink *devlink);
+int devlink_port_params_register(struct devlink_port *devlink_port,
+                                const struct devlink_param *params,
+                                size_t params_count);
+void devlink_port_params_unregister(struct devlink_port *devlink_port,
+                                   const struct devlink_param *params,
+                                   size_t params_count);
 int devlink_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
                                       union devlink_param_value *init_val);
 int devlink_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
                                       union devlink_param_value init_val);
+int
+devlink_port_param_driverinit_value_get(struct devlink_port *devlink_port,
+                                       u32 param_id,
+                                       union devlink_param_value *init_val);
+int devlink_port_param_driverinit_value_set(struct devlink_port *devlink_port,
+                                           u32 param_id,
+                                           union devlink_param_value init_val);
 void devlink_param_value_changed(struct devlink *devlink, u32 param_id);
+void devlink_port_param_value_changed(struct devlink_port *devlink_port,
+                                     u32 param_id);
 void devlink_param_value_str_fill(union devlink_param_value *dst_val,
                                  const char *src);
 struct devlink_region *devlink_region_create(struct devlink *devlink,
@@ -583,6 +649,63 @@ u32 devlink_region_shapshot_id_get(struct devlink *devlink);
 int devlink_region_snapshot_create(struct devlink_region *region, u64 data_len,
                                   u8 *data, u32 snapshot_id,
                                   devlink_snapshot_data_dest_t *data_destructor);
+int devlink_info_serial_number_put(struct devlink_info_req *req,
+                                  const char *sn);
+int devlink_info_driver_name_put(struct devlink_info_req *req,
+                                const char *name);
+int devlink_info_version_fixed_put(struct devlink_info_req *req,
+                                  const char *version_name,
+                                  const char *version_value);
+int devlink_info_version_stored_put(struct devlink_info_req *req,
+                                   const char *version_name,
+                                   const char *version_value);
+int devlink_info_version_running_put(struct devlink_info_req *req,
+                                    const char *version_name,
+                                    const char *version_value);
+
+int devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg);
+int devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg);
+
+int devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name);
+int devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg);
+
+int devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
+                                    const char *name);
+int devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg);
+
+int devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value);
+int devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value);
+int devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value);
+int devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value);
+int devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value);
+int devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
+                           u16 value_len);
+
+int devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                              bool value);
+int devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                            u8 value);
+int devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                             u32 value);
+int devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                             u64 value);
+int devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                                const char *value);
+int devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                                const void *value, u16 value_len);
+
+struct devlink_health_reporter *
+devlink_health_reporter_create(struct devlink *devlink,
+                              const struct devlink_health_reporter_ops *ops,
+                              u64 graceful_period, bool auto_recover,
+                              void *priv);
+void
+devlink_health_reporter_destroy(struct devlink_health_reporter *reporter);
+
+void *
+devlink_health_reporter_priv(struct devlink_health_reporter *reporter);
+int devlink_health_report(struct devlink_health_reporter *reporter,
+                         const char *msg, void *priv_ctx);
 
 #else
 
@@ -601,6 +724,14 @@ static inline void devlink_unregister(struct devlink *devlink)
 {
 }
 
+static inline void devlink_params_publish(struct devlink *devlink)
+{
+}
+
+static inline void devlink_params_unpublish(struct devlink *devlink)
+{
+}
+
 static inline void devlink_free(struct devlink *devlink)
 {
        kfree(devlink);
@@ -792,6 +923,21 @@ devlink_params_unregister(struct devlink *devlink,
 }
 
 static inline int
+devlink_port_params_register(struct devlink_port *devlink_port,
+                            const struct devlink_param *params,
+                            size_t params_count)
+{
+       return 0;
+}
+
+static inline void
+devlink_port_params_unregister(struct devlink_port *devlink_port,
+                              const struct devlink_param *params,
+                              size_t params_count)
+{
+}
+
+static inline int
 devlink_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
                                   union devlink_param_value *init_val)
 {
@@ -805,12 +951,34 @@ devlink_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
        return -EOPNOTSUPP;
 }
 
+static inline int
+devlink_port_param_driverinit_value_get(struct devlink_port *devlink_port,
+                                       u32 param_id,
+                                       union devlink_param_value *init_val)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int
+devlink_port_param_driverinit_value_set(struct devlink_port *devlink_port,
+                                       u32 param_id,
+                                       union devlink_param_value init_val)
+{
+       return -EOPNOTSUPP;
+}
+
 static inline void
 devlink_param_value_changed(struct devlink *devlink, u32 param_id)
 {
 }
 
 static inline void
+devlink_port_param_value_changed(struct devlink_port *devlink_port,
+                                u32 param_id)
+{
+}
+
+static inline void
 devlink_param_value_str_fill(union devlink_param_value *dst_val,
                             const char *src)
 {
@@ -844,6 +1012,201 @@ devlink_region_snapshot_create(struct devlink_region *region, u64 data_len,
        return 0;
 }
 
+static inline int
+devlink_info_driver_name_put(struct devlink_info_req *req, const char *name)
+{
+       return 0;
+}
+
+static inline int
+devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn)
+{
+       return 0;
+}
+
+static inline int
+devlink_info_version_fixed_put(struct devlink_info_req *req,
+                              const char *version_name,
+                              const char *version_value)
+{
+       return 0;
+}
+
+static inline int
+devlink_info_version_stored_put(struct devlink_info_req *req,
+                               const char *version_name,
+                               const char *version_value)
+{
+       return 0;
+}
+
+static inline int
+devlink_info_version_running_put(struct devlink_info_req *req,
+                                const char *version_name,
+                                const char *version_value)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
+                                const char *name)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
+                       u16 value_len)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                          bool value)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                        u8 value)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                         u32 value)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                         u64 value)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                            const char *value)
+{
+       return 0;
+}
+
+static inline int
+devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                            const void *value, u16 value_len)
+{
+       return 0;
+}
+
+static inline struct devlink_health_reporter *
+devlink_health_reporter_create(struct devlink *devlink,
+                              const struct devlink_health_reporter_ops *ops,
+                              u64 graceful_period, bool auto_recover,
+                              void *priv)
+{
+       return NULL;
+}
+
+static inline void
+devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
+{
+}
+
+static inline void *
+devlink_health_reporter_priv(struct devlink_health_reporter *reporter)
+{
+       return NULL;
+}
+
+static inline int
+devlink_health_report(struct devlink_health_reporter *reporter,
+                     const char *msg, void *priv_ctx)
+{
+       return 0;
+}
+#endif
+
+#if IS_REACHABLE(CONFIG_NET_DEVLINK)
+void devlink_compat_running_version(struct net_device *dev,
+                                   char *buf, size_t len);
+int devlink_compat_flash_update(struct net_device *dev, const char *file_name);
+#else
+static inline void
+devlink_compat_running_version(struct net_device *dev, char *buf, size_t len)
+{
+}
+
+static inline int
+devlink_compat_flash_update(struct net_device *dev, const char *file_name)
+{
+       return -EOPNOTSUPP;
+}
 #endif
 
 #endif /* _NET_DEVLINK_H_ */
diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h
new file mode 100644 (file)
index 0000000..d035183
--- /dev/null
@@ -0,0 +1,203 @@
+#ifndef _NET_FLOW_OFFLOAD_H
+#define _NET_FLOW_OFFLOAD_H
+
+#include <net/flow_dissector.h>
+
+struct flow_match {
+       struct flow_dissector   *dissector;
+       void                    *mask;
+       void                    *key;
+};
+
+struct flow_match_basic {
+       struct flow_dissector_key_basic *key, *mask;
+};
+
+struct flow_match_control {
+       struct flow_dissector_key_control *key, *mask;
+};
+
+struct flow_match_eth_addrs {
+       struct flow_dissector_key_eth_addrs *key, *mask;
+};
+
+struct flow_match_vlan {
+       struct flow_dissector_key_vlan *key, *mask;
+};
+
+struct flow_match_ipv4_addrs {
+       struct flow_dissector_key_ipv4_addrs *key, *mask;
+};
+
+struct flow_match_ipv6_addrs {
+       struct flow_dissector_key_ipv6_addrs *key, *mask;
+};
+
+struct flow_match_ip {
+       struct flow_dissector_key_ip *key, *mask;
+};
+
+struct flow_match_ports {
+       struct flow_dissector_key_ports *key, *mask;
+};
+
+struct flow_match_icmp {
+       struct flow_dissector_key_icmp *key, *mask;
+};
+
+struct flow_match_tcp {
+       struct flow_dissector_key_tcp *key, *mask;
+};
+
+struct flow_match_mpls {
+       struct flow_dissector_key_mpls *key, *mask;
+};
+
+struct flow_match_enc_keyid {
+       struct flow_dissector_key_keyid *key, *mask;
+};
+
+struct flow_match_enc_opts {
+       struct flow_dissector_key_enc_opts *key, *mask;
+};
+
+struct flow_rule;
+
+void flow_rule_match_basic(const struct flow_rule *rule,
+                          struct flow_match_basic *out);
+void flow_rule_match_control(const struct flow_rule *rule,
+                            struct flow_match_control *out);
+void flow_rule_match_eth_addrs(const struct flow_rule *rule,
+                              struct flow_match_eth_addrs *out);
+void flow_rule_match_vlan(const struct flow_rule *rule,
+                         struct flow_match_vlan *out);
+void flow_rule_match_ipv4_addrs(const struct flow_rule *rule,
+                               struct flow_match_ipv4_addrs *out);
+void flow_rule_match_ipv6_addrs(const struct flow_rule *rule,
+                               struct flow_match_ipv6_addrs *out);
+void flow_rule_match_ip(const struct flow_rule *rule,
+                       struct flow_match_ip *out);
+void flow_rule_match_ports(const struct flow_rule *rule,
+                          struct flow_match_ports *out);
+void flow_rule_match_tcp(const struct flow_rule *rule,
+                        struct flow_match_tcp *out);
+void flow_rule_match_icmp(const struct flow_rule *rule,
+                         struct flow_match_icmp *out);
+void flow_rule_match_mpls(const struct flow_rule *rule,
+                         struct flow_match_mpls *out);
+void flow_rule_match_enc_control(const struct flow_rule *rule,
+                                struct flow_match_control *out);
+void flow_rule_match_enc_ipv4_addrs(const struct flow_rule *rule,
+                                   struct flow_match_ipv4_addrs *out);
+void flow_rule_match_enc_ipv6_addrs(const struct flow_rule *rule,
+                                   struct flow_match_ipv6_addrs *out);
+void flow_rule_match_enc_ip(const struct flow_rule *rule,
+                           struct flow_match_ip *out);
+void flow_rule_match_enc_ports(const struct flow_rule *rule,
+                              struct flow_match_ports *out);
+void flow_rule_match_enc_keyid(const struct flow_rule *rule,
+                              struct flow_match_enc_keyid *out);
+void flow_rule_match_enc_opts(const struct flow_rule *rule,
+                             struct flow_match_enc_opts *out);
+
+enum flow_action_id {
+       FLOW_ACTION_ACCEPT              = 0,
+       FLOW_ACTION_DROP,
+       FLOW_ACTION_TRAP,
+       FLOW_ACTION_GOTO,
+       FLOW_ACTION_REDIRECT,
+       FLOW_ACTION_MIRRED,
+       FLOW_ACTION_VLAN_PUSH,
+       FLOW_ACTION_VLAN_POP,
+       FLOW_ACTION_VLAN_MANGLE,
+       FLOW_ACTION_TUNNEL_ENCAP,
+       FLOW_ACTION_TUNNEL_DECAP,
+       FLOW_ACTION_MANGLE,
+       FLOW_ACTION_ADD,
+       FLOW_ACTION_CSUM,
+       FLOW_ACTION_MARK,
+       FLOW_ACTION_WAKE,
+       FLOW_ACTION_QUEUE,
+};
+
+/* This is mirroring enum pedit_header_type definition for easy mapping between
+ * tc pedit action. Legacy TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK is mapped to
+ * FLOW_ACT_MANGLE_UNSPEC, which is supported by no driver.
+ */
+enum flow_action_mangle_base {
+       FLOW_ACT_MANGLE_UNSPEC          = 0,
+       FLOW_ACT_MANGLE_HDR_TYPE_ETH,
+       FLOW_ACT_MANGLE_HDR_TYPE_IP4,
+       FLOW_ACT_MANGLE_HDR_TYPE_IP6,
+       FLOW_ACT_MANGLE_HDR_TYPE_TCP,
+       FLOW_ACT_MANGLE_HDR_TYPE_UDP,
+};
+
+struct flow_action_entry {
+       enum flow_action_id             id;
+       union {
+               u32                     chain_index;    /* FLOW_ACTION_GOTO */
+               struct net_device       *dev;           /* FLOW_ACTION_REDIRECT */
+               struct {                                /* FLOW_ACTION_VLAN */
+                       u16             vid;
+                       __be16          proto;
+                       u8              prio;
+               } vlan;
+               struct {                                /* FLOW_ACTION_PACKET_EDIT */
+                       enum flow_action_mangle_base htype;
+                       u32             offset;
+                       u32             mask;
+                       u32             val;
+               } mangle;
+               const struct ip_tunnel_info *tunnel;    /* FLOW_ACTION_TUNNEL_ENCAP */
+               u32                     csum_flags;     /* FLOW_ACTION_CSUM */
+               u32                     mark;           /* FLOW_ACTION_MARK */
+               struct {                                /* FLOW_ACTION_QUEUE */
+                       u32             ctx;
+                       u32             index;
+                       u8              vf;
+               } queue;
+       };
+};
+
+struct flow_action {
+       unsigned int                    num_entries;
+       struct flow_action_entry        entries[0];
+};
+
+static inline bool flow_action_has_entries(const struct flow_action *action)
+{
+       return action->num_entries;
+}
+
+#define flow_action_for_each(__i, __act, __actions)                    \
+        for (__i = 0, __act = &(__actions)->entries[0]; __i < (__actions)->num_entries; __act = &(__actions)->entries[++__i])
+
+struct flow_rule {
+       struct flow_match       match;
+       struct flow_action      action;
+};
+
+struct flow_rule *flow_rule_alloc(unsigned int num_actions);
+
+static inline bool flow_rule_match_key(const struct flow_rule *rule,
+                                      enum flow_dissector_key_id key)
+{
+       return dissector_uses_key(rule->match.dissector, key);
+}
+
+struct flow_stats {
+       u64     pkts;
+       u64     bytes;
+       u64     lastused;
+};
+
+static inline void flow_stats_update(struct flow_stats *flow_stats,
+                                    u64 bytes, u64 pkts, u64 lastused)
+{
+       flow_stats->pkts        += pkts;
+       flow_stats->bytes       += bytes;
+       flow_stats->lastused    = max_t(u64, flow_stats->lastused, lastused);
+}
+
+#endif /* _NET_FLOW_OFFLOAD_H */
index 00b5e78..74ff688 100644 (file)
@@ -39,6 +39,7 @@ struct inet_peer {
 
        u32                     metrics[RTAX_MAX];
        u32                     rate_tokens;    /* rate limiting for ICMP */
+       u32                     n_redirects;
        unsigned long           rate_last;
        /*
         * Once inet_peer is queued for deletion (refcnt == 0), following field
index 78fa0ac..5175fd6 100644 (file)
@@ -153,7 +153,8 @@ struct sk_buff *l3mdev_l3_rcv(struct sk_buff *skb, u16 proto)
 
        if (netif_is_l3_slave(skb->dev))
                master = netdev_master_upper_dev_get_rcu(skb->dev);
-       else if (netif_is_l3_master(skb->dev))
+       else if (netif_is_l3_master(skb->dev) ||
+                netif_has_l3_rx_handler(skb->dev))
                master = skb->dev;
 
        if (master && master->l3mdev_ops->l3mdev_l3_rcv)
index 33fd9ba..671113b 100644 (file)
@@ -126,6 +126,8 @@ int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b);
 int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb);
 int lwtunnel_input(struct sk_buff *skb);
 int lwtunnel_xmit(struct sk_buff *skb);
+int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len,
+                         bool ingress);
 
 static inline void lwtunnel_set_redirect(struct dst_entry *dst)
 {
index 88219cc..de866a7 100644 (file)
  * The driver is expected to initialize its private per-queue data for stations
  * and interfaces in the .add_interface and .sta_add ops.
  *
- * The driver can't access the queue directly. To dequeue a frame, it calls
- * ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a queue, it
- * calls the .wake_tx_queue driver op.
+ * The driver can't access the queue directly. To dequeue a frame from a
+ * txq, it calls ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a
+ * queue, it calls the .wake_tx_queue driver op.
+ *
+ * Drivers can optionally delegate responsibility for scheduling queues to
+ * mac80211, to take advantage of airtime fairness accounting. In this case, to
+ * obtain the next queue to pull frames from, the driver calls
+ * ieee80211_next_txq(). The driver is then expected to return the txq using
+ * ieee80211_return_txq().
  *
  * For AP powersave TIM handling, the driver only needs to indicate if it has
  * buffered packets in the driver specific data structures by calling
@@ -936,8 +942,32 @@ ieee80211_rate_get_vht_nss(const struct ieee80211_tx_rate *rate)
  * @band: the band to transmit on (use for checking for races)
  * @hw_queue: HW queue to put the frame on, skb_get_queue_mapping() gives the AC
  * @ack_frame_id: internal frame ID for TX status, used internally
- * @control: union for control data
- * @status: union for status data
+ * @control: union part for control data
+ * @control.rates: TX rates array to try
+ * @control.rts_cts_rate_idx: rate for RTS or CTS
+ * @control.use_rts: use RTS
+ * @control.use_cts_prot: use RTS/CTS
+ * @control.short_preamble: use short preamble (CCK only)
+ * @control.skip_table: skip externally configured rate table
+ * @control.jiffies: timestamp for expiry on powersave clients
+ * @control.vif: virtual interface (may be NULL)
+ * @control.hw_key: key to encrypt with (may be NULL)
+ * @control.flags: control flags, see &enum mac80211_tx_control_flags
+ * @control.enqueue_time: enqueue time (for iTXQs)
+ * @driver_rates: alias to @control.rates to reserve space
+ * @pad: padding
+ * @rate_driver_data: driver use area if driver needs @control.rates
+ * @status: union part for status data
+ * @status.rates: attempted rates
+ * @status.ack_signal: ACK signal
+ * @status.ampdu_ack_len: AMPDU ack length
+ * @status.ampdu_len: AMPDU length
+ * @status.antenna: (legacy, kept only for iwlegacy)
+ * @status.tx_time: airtime consumed for transmission
+ * @status.is_valid_ack_signal: ACK signal is valid
+ * @status.status_driver_data: driver use area
+ * @ack: union part for pure ACK data
+ * @ack.cookie: cookie for the ACK
  * @driver_data: array of driver_data pointers
  * @ampdu_ack_len: number of acked aggregated frames.
  *     relevant only if IEEE80211_TX_STAT_AMPDU was set.
@@ -1157,6 +1187,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_AMPDU_EOF_BIT_KNOWN: The EOF value is known
  * @RX_FLAG_RADIOTAP_HE: HE radiotap data is present
  *     (&struct ieee80211_radiotap_he, mac80211 will fill in
+ *     
  *      - DATA3_DATA_MCS
  *      - DATA3_DATA_DCM
  *      - DATA3_CODING
@@ -1164,6 +1195,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  *      - DATA5_DATA_BW_RU_ALLOC
  *      - DATA6_NSTS
  *      - DATA3_STBC
+ *     
  *     from the RX info data, so leave those zeroed when building this data)
  * @RX_FLAG_RADIOTAP_HE_MU: HE MU radiotap data is present
  *     (&struct ieee80211_radiotap_he_mu)
@@ -1214,7 +1246,7 @@ enum mac80211_rx_flags {
  * @RX_ENC_FLAG_HT_GF: This frame was received in a HT-greenfield transmission,
  *     if the driver fills this value it should add
  *     %IEEE80211_RADIOTAP_MCS_HAVE_FMT
- *     to hw.radiotap_mcs_details to advertise that fact
+ *     to @hw.radiotap_mcs_details to advertise that fact.
  * @RX_ENC_FLAG_LDPC: LDPC was used
  * @RX_ENC_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
  * @RX_ENC_FLAG_BF: packet was beamformed
@@ -2184,6 +2216,9 @@ struct ieee80211_txq {
  *     MMPDUs on station interfaces. This of course requires the driver to use
  *     TXQs to start with.
  *
+ * @IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN: Driver does not report accurate A-MPDU
+ *     length in tx status information
+ *
  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
  */
 enum ieee80211_hw_flags {
@@ -2232,6 +2267,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_BUFF_MMPDU_TXQ,
        IEEE80211_HW_SUPPORTS_VHT_EXT_NSS_BW,
        IEEE80211_HW_STA_MMPDU_TXQ,
+       IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
 
        /* keep last, obviously */
        NUM_IEEE80211_HW_FLAGS
@@ -2323,12 +2359,14 @@ enum ieee80211_hw_flags {
  * @radiotap_he: HE radiotap validity flags
  *
  * @radiotap_timestamp: Information for the radiotap timestamp field; if the
- *     'units_pos' member is set to a non-negative value it must be set to
- *     a combination of a IEEE80211_RADIOTAP_TIMESTAMP_UNIT_* and a
- *     IEEE80211_RADIOTAP_TIMESTAMP_SPOS_* value, and then the timestamp
+ *     @units_pos member is set to a non-negative value then the timestamp
  *     field will be added and populated from the &struct ieee80211_rx_status
- *     device_timestamp. If the 'accuracy' member is non-negative, it's put
- *     into the accuracy radiotap field and the accuracy known flag is set.
+ *     device_timestamp.
+ * @radiotap_timestamp.units_pos: Must be set to a combination of a
+ *     IEEE80211_RADIOTAP_TIMESTAMP_UNIT_* and a
+ *     IEEE80211_RADIOTAP_TIMESTAMP_SPOS_* value.
+ * @radiotap_timestamp.accuracy: If non-negative, fills the accuracy in the
+ *     radiotap field and the accuracy known flag will be set.
  *
  * @netdev_features: netdev features to be set in each netdev created
  *     from this HW. Note that not all features are usable with mac80211,
@@ -2354,6 +2392,9 @@ enum ieee80211_hw_flags {
  * @tx_sk_pacing_shift: Pacing shift to set on TCP sockets when frames from
  *     them are encountered. The default should typically not be changed,
  *     unless the driver has good reasons for needing more buffers.
+ *
+ * @weight_multiplier: Driver specific airtime weight multiplier used while
+ *     refilling deficit of each TXQ.
  */
 struct ieee80211_hw {
        struct ieee80211_conf conf;
@@ -2390,6 +2431,7 @@ struct ieee80211_hw {
        const struct ieee80211_cipher_scheme *cipher_schemes;
        u8 max_nan_de_entries;
        u8 tx_sk_pacing_shift;
+       u8 weight_multiplier;
 };
 
 static inline bool _ieee80211_hw_check(struct ieee80211_hw *hw,
@@ -5402,6 +5444,34 @@ void ieee80211_sta_eosp(struct ieee80211_sta *pubsta);
 void ieee80211_send_eosp_nullfunc(struct ieee80211_sta *pubsta, int tid);
 
 /**
+ * ieee80211_sta_register_airtime - register airtime usage for a sta/tid
+ *
+ * Register airtime usage for a given sta on a given tid. The driver can call
+ * this function to notify mac80211 that a station used a certain amount of
+ * airtime. This information will be used by the TXQ scheduler to schedule
+ * stations in a way that ensures airtime fairness.
+ *
+ * The reported airtime should as a minimum include all time that is spent
+ * transmitting to the remote station, including overhead and padding, but not
+ * including time spent waiting for a TXOP. If the time is not reported by the
+ * hardware it can in some cases be calculated from the rate and known frame
+ * composition. When possible, the time should include any failed transmission
+ * attempts.
+ *
+ * The driver can either call this function synchronously for every packet or
+ * aggregate, or asynchronously as airtime usage information becomes available.
+ * TX and RX airtime can be reported together, or separately by setting one of
+ * them to 0.
+ *
+ * @pubsta: the station
+ * @tid: the TID to register airtime for
+ * @tx_airtime: airtime used during TX (in usec)
+ * @rx_airtime: airtime used during RX (in usec)
+ */
+void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
+                                   u32 tx_airtime, u32 rx_airtime);
+
+/**
  * ieee80211_iter_keys - iterate keys programmed into the device
  * @hw: pointer obtained from ieee80211_alloc_hw()
  * @vif: virtual interface to iterate, may be %NULL for all
@@ -6103,7 +6173,8 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
  * ieee80211_tx_dequeue - dequeue a packet from a software tx queue
  *
  * @hw: pointer as obtained from ieee80211_alloc_hw()
- * @txq: pointer obtained from station or virtual interface
+ * @txq: pointer obtained from station or virtual interface, or from
+ *     ieee80211_next_txq()
  *
  * Returns the skb if successful, %NULL if no frame was available.
  *
@@ -6119,6 +6190,94 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
                                     struct ieee80211_txq *txq);
 
 /**
+ * ieee80211_next_txq - get next tx queue to pull packets from
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @ac: AC number to return packets from.
+ *
+ * Should only be called between calls to ieee80211_txq_schedule_start()
+ * and ieee80211_txq_schedule_end().
+ * Returns the next txq if successful, %NULL if no queue is eligible. If a txq
+ * is returned, it should be returned with ieee80211_return_txq() after the
+ * driver has finished scheduling it.
+ */
+struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac);
+
+/**
+ * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @txq: pointer obtained from station or virtual interface
+ *
+ * Should only be called between calls to ieee80211_txq_schedule_start()
+ * and ieee80211_txq_schedule_end().
+ */
+void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
+
+/**
+ * ieee80211_txq_schedule_start - acquire locks for safe scheduling of an AC
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @ac: AC number to acquire locks for
+ *
+ * Acquire locks needed to schedule TXQs from the given AC. Should be called
+ * before ieee80211_next_txq() or ieee80211_return_txq().
+ */
+void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
+       __acquires(txq_lock);
+
+/**
+ * ieee80211_txq_schedule_end - release locks for safe scheduling of an AC
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @ac: AC number to acquire locks for
+ *
+ * Release locks previously acquired by ieee80211_txq_schedule_end().
+ */
+void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
+       __releases(txq_lock);
+
+/**
+ * ieee80211_schedule_txq - schedule a TXQ for transmission
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @txq: pointer obtained from station or virtual interface
+ *
+ * Schedules a TXQ for transmission if it is not already scheduled. Takes a
+ * lock, which means it must *not* be called between
+ * ieee80211_txq_schedule_start() and ieee80211_txq_schedule_end()
+ */
+void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
+       __acquires(txq_lock) __releases(txq_lock);
+
+/**
+ * ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
+ *
+ * This function is used to check whether given txq is allowed to transmit by
+ * the airtime scheduler, and can be used by drivers to access the airtime
+ * fairness accounting without going using the scheduling order enfored by
+ * next_txq().
+ *
+ * Returns %true if the airtime scheduler thinks the TXQ should be allowed to
+ * transmit, and %false if it should be throttled. This function can also have
+ * the side effect of rotating the TXQ in the scheduler rotation, which will
+ * eventually bring the deficit to positive and allow the station to transmit
+ * again.
+ *
+ * The API ieee80211_txq_may_transmit() also ensures that TXQ list will be
+ * aligned aginst driver's own round-robin scheduler list. i.e it rotates
+ * the TXQ list till it makes the requested node becomes the first entry
+ * in TXQ list. Thus both the TXQ list and driver's list are in sync. If this
+ * function returns %true, the driver is expected to schedule packets
+ * for transmission, and then return the TXQ through ieee80211_return_txq().
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @txq: pointer obtained from station or virtual interface
+ */
+bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
+                               struct ieee80211_txq *txq);
+
+/**
  * ieee80211_txq_get_depth - get pending frame/byte count of given txq
  *
  * The values are not guaranteed to be coherent with regard to each other, i.e.
index 45eba7d..a66fcd3 100644 (file)
@@ -469,9 +469,7 @@ struct nft_set_binding {
 int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
                       struct nft_set_binding *binding);
 void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
-                         struct nft_set_binding *binding);
-void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set,
-                         struct nft_set_binding *binding);
+                         struct nft_set_binding *binding, bool commit);
 void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set);
 
 /**
@@ -721,6 +719,13 @@ struct nft_expr_type {
 #define NFT_EXPR_STATEFUL              0x1
 #define NFT_EXPR_GC                    0x2
 
+enum nft_trans_phase {
+       NFT_TRANS_PREPARE,
+       NFT_TRANS_ABORT,
+       NFT_TRANS_COMMIT,
+       NFT_TRANS_RELEASE
+};
+
 /**
  *     struct nft_expr_ops - nf_tables expression operations
  *
@@ -750,7 +755,8 @@ struct nft_expr_ops {
        void                            (*activate)(const struct nft_ctx *ctx,
                                                    const struct nft_expr *expr);
        void                            (*deactivate)(const struct nft_ctx *ctx,
-                                                     const struct nft_expr *expr);
+                                                     const struct nft_expr *expr,
+                                                     enum nft_trans_phase phase);
        void                            (*destroy)(const struct nft_ctx *ctx,
                                                   const struct nft_expr *expr);
        void                            (*destroy_clone)(const struct nft_ctx *ctx,
@@ -1335,12 +1341,15 @@ struct nft_trans_rule {
 struct nft_trans_set {
        struct nft_set                  *set;
        u32                             set_id;
+       bool                            bound;
 };
 
 #define nft_trans_set(trans)   \
        (((struct nft_trans_set *)trans->data)->set)
 #define nft_trans_set_id(trans)        \
        (((struct nft_trans_set *)trans->data)->set_id)
+#define nft_trans_set_bound(trans)     \
+       (((struct nft_trans_set *)trans->data)->bound)
 
 struct nft_trans_chain {
        bool                            update;
index 4c1e993..23f27b0 100644 (file)
@@ -306,10 +306,14 @@ struct nla_policy {
 #define NLA_POLICY_ETH_ADDR            NLA_POLICY_EXACT_LEN(ETH_ALEN)
 #define NLA_POLICY_ETH_ADDR_COMPAT     NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN)
 
-#define NLA_POLICY_NESTED(maxattr, policy) \
+#define _NLA_POLICY_NESTED(maxattr, policy) \
        { .type = NLA_NESTED, .validation_data = policy, .len = maxattr }
-#define NLA_POLICY_NESTED_ARRAY(maxattr, policy) \
+#define _NLA_POLICY_NESTED_ARRAY(maxattr, policy) \
        { .type = NLA_NESTED_ARRAY, .validation_data = policy, .len = maxattr }
+#define NLA_POLICY_NESTED(policy) \
+       _NLA_POLICY_NESTED(ARRAY_SIZE(policy) - 1, policy)
+#define NLA_POLICY_NESTED_ARRAY(policy) \
+       _NLA_POLICY_NESTED_ARRAY(ARRAY_SIZE(policy) - 1, policy)
 
 #define __NLA_ENSURE(condition) BUILD_BUG_ON_ZERO(!(condition))
 #define NLA_ENSURE_INT_TYPE(tp)                                \
index 40965fb..6a530be 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/workqueue.h>
 #include <net/sch_generic.h>
 #include <net/act_api.h>
+#include <net/flow_offload.h>
 
 /* TC action not accessible from user space */
 #define TC_ACT_REINSERT                (TC_ACT_VALUE_MAX + 1)
@@ -43,6 +44,10 @@ bool tcf_queue_work(struct rcu_work *rwork, work_func_t func);
 struct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block,
                                       u32 chain_index);
 void tcf_chain_put_by_act(struct tcf_chain *chain);
+struct tcf_chain *tcf_get_next_chain(struct tcf_block *block,
+                                    struct tcf_chain *chain);
+struct tcf_proto *tcf_get_next_proto(struct tcf_chain *chain,
+                                    struct tcf_proto *tp, bool rtnl_held);
 void tcf_block_netif_keep_dst(struct tcf_block *block);
 int tcf_block_get(struct tcf_block **p_block,
                  struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
@@ -411,7 +416,7 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts,
 
 int tcf_exts_validate(struct net *net, struct tcf_proto *tp,
                      struct nlattr **tb, struct nlattr *rate_tlv,
-                     struct tcf_exts *exts, bool ovr,
+                     struct tcf_exts *exts, bool ovr, bool rtnl_held,
                      struct netlink_ext_ack *extack);
 void tcf_exts_destroy(struct tcf_exts *exts);
 void tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src);
@@ -619,8 +624,11 @@ tcf_match_indev(struct sk_buff *skb, int ifindex)
 }
 #endif /* CONFIG_NET_CLS_IND */
 
+int tc_setup_flow_action(struct flow_action *flow_action,
+                        const struct tcf_exts *exts);
 int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
                     void *type_data, bool err_stop);
+unsigned int tcf_exts_num_actions(struct tcf_exts *exts);
 
 enum tc_block_command {
        TC_BLOCK_BIND,
@@ -760,13 +768,17 @@ struct tc_cls_flower_offload {
        struct tc_cls_common_offload common;
        enum tc_fl_command command;
        unsigned long cookie;
-       struct flow_dissector *dissector;
-       struct fl_flow_key *mask;
-       struct fl_flow_key *key;
-       struct tcf_exts *exts;
+       struct flow_rule *rule;
+       struct flow_stats stats;
        u32 classid;
 };
 
+static inline struct flow_rule *
+tc_cls_flower_offload_flow_rule(struct tc_cls_flower_offload *tc_flow_cmd)
+{
+       return tc_flow_cmd->rule;
+}
+
 enum tc_matchall_command {
        TC_CLSMATCHALL_REPLACE,
        TC_CLSMATCHALL_DESTROY,
index 7a49575..e50b729 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/list.h>
 #include <linux/refcount.h>
 #include <linux/workqueue.h>
+#include <linux/mutex.h>
 #include <net/gen_stats.h>
 #include <net/rtnetlink.h>
 
@@ -178,6 +179,7 @@ static inline int qdisc_avail_bulklimit(const struct netdev_queue *txq)
 }
 
 struct Qdisc_class_ops {
+       unsigned int            flags;
        /* Child qdisc manipulation */
        struct netdev_queue *   (*select_queue)(struct Qdisc *, struct tcmsg *);
        int                     (*graft)(struct Qdisc *, unsigned long cl,
@@ -209,6 +211,13 @@ struct Qdisc_class_ops {
                                        struct gnet_dump *);
 };
 
+/* Qdisc_class_ops flag values */
+
+/* Implements API that doesn't require rtnl lock */
+enum qdisc_class_ops_flags {
+       QDISC_CLASS_OPS_DOIT_UNLOCKED = 1,
+};
+
 struct Qdisc_ops {
        struct Qdisc_ops        *next;
        const struct Qdisc_class_ops    *cl_ops;
@@ -272,19 +281,21 @@ struct tcf_proto_ops {
                                            const struct tcf_proto *,
                                            struct tcf_result *);
        int                     (*init)(struct tcf_proto*);
-       void                    (*destroy)(struct tcf_proto *tp,
+       void                    (*destroy)(struct tcf_proto *tp, bool rtnl_held,
                                           struct netlink_ext_ack *extack);
 
        void*                   (*get)(struct tcf_proto*, u32 handle);
+       void                    (*put)(struct tcf_proto *tp, void *f);
        int                     (*change)(struct net *net, struct sk_buff *,
                                        struct tcf_proto*, unsigned long,
                                        u32 handle, struct nlattr **,
-                                       void **, bool,
+                                       void **, bool, bool,
                                        struct netlink_ext_ack *);
        int                     (*delete)(struct tcf_proto *tp, void *arg,
-                                         bool *last,
+                                         bool *last, bool rtnl_held,
                                          struct netlink_ext_ack *);
-       void                    (*walk)(struct tcf_proto*, struct tcf_walker *arg);
+       void                    (*walk)(struct tcf_proto *tp,
+                                       struct tcf_walker *arg, bool rtnl_held);
        int                     (*reoffload)(struct tcf_proto *tp, bool add,
                                             tc_setup_cb_t *cb, void *cb_priv,
                                             struct netlink_ext_ack *extack);
@@ -297,12 +308,18 @@ struct tcf_proto_ops {
 
        /* rtnetlink specific */
        int                     (*dump)(struct net*, struct tcf_proto*, void *,
-                                       struct sk_buff *skb, struct tcmsg*);
+                                       struct sk_buff *skb, struct tcmsg*,
+                                       bool);
        int                     (*tmplt_dump)(struct sk_buff *skb,
                                              struct net *net,
                                              void *tmplt_priv);
 
        struct module           *owner;
+       int                     flags;
+};
+
+enum tcf_proto_ops_flags {
+       TCF_PROTO_OPS_DOIT_UNLOCKED = 1,
 };
 
 struct tcf_proto {
@@ -321,6 +338,12 @@ struct tcf_proto {
        void                    *data;
        const struct tcf_proto_ops      *ops;
        struct tcf_chain        *chain;
+       /* Lock protects tcf_proto shared state and can be used by unlocked
+        * classifiers to protect their private data.
+        */
+       spinlock_t              lock;
+       bool                    deleting;
+       refcount_t              refcnt;
        struct rcu_head         rcu;
 };
 
@@ -340,6 +363,8 @@ struct qdisc_skb_cb {
 typedef void tcf_chain_head_change_t(struct tcf_proto *tp_head, void *priv);
 
 struct tcf_chain {
+       /* Protects filter_chain. */
+       struct mutex filter_chain_lock;
        struct tcf_proto __rcu *filter_chain;
        struct list_head list;
        struct tcf_block *block;
@@ -347,11 +372,16 @@ struct tcf_chain {
        unsigned int refcnt;
        unsigned int action_refcnt;
        bool explicitly_created;
+       bool flushing;
        const struct tcf_proto_ops *tmplt_ops;
        void *tmplt_priv;
 };
 
 struct tcf_block {
+       /* Lock protects tcf_block and lifetime-management data of chains
+        * attached to the block (refcnt, action_refcnt, explicitly_created).
+        */
+       struct mutex lock;
        struct list_head chain_list;
        u32 index; /* block index for shared blocks */
        refcount_t refcnt;
@@ -369,6 +399,34 @@ struct tcf_block {
        struct rcu_head rcu;
 };
 
+#ifdef CONFIG_PROVE_LOCKING
+static inline bool lockdep_tcf_chain_is_locked(struct tcf_chain *chain)
+{
+       return lockdep_is_held(&chain->filter_chain_lock);
+}
+
+static inline bool lockdep_tcf_proto_is_locked(struct tcf_proto *tp)
+{
+       return lockdep_is_held(&tp->lock);
+}
+#else
+static inline bool lockdep_tcf_chain_is_locked(struct tcf_block *chain)
+{
+       return true;
+}
+
+static inline bool lockdep_tcf_proto_is_locked(struct tcf_proto *tp)
+{
+       return true;
+}
+#endif /* #ifdef CONFIG_PROVE_LOCKING */
+
+#define tcf_chain_dereference(p, chain)                                        \
+       rcu_dereference_protected(p, lockdep_tcf_chain_is_locked(chain))
+
+#define tcf_proto_dereference(p, tp)                                   \
+       rcu_dereference_protected(p, lockdep_tcf_proto_is_locked(tp))
+
 static inline void tcf_block_offload_inc(struct tcf_block *block, u32 *flags)
 {
        if (*flags & TCA_CLS_FLAGS_IN_HW)
index 003020e..58e4b23 100644 (file)
@@ -199,6 +199,8 @@ struct sctp_sock {
        __u32 flowlabel;
        __u8  dscp;
 
+       int pf_retrans;
+
        /* The initial Path MTU to use for new associations. */
        __u32 pathmtu;
 
@@ -209,6 +211,8 @@ struct sctp_sock {
        /* Flags controlling Heartbeat, SACK delay, and Path MTU Discovery. */
        __u32 param_flags;
 
+       __u32 default_ss;
+
        struct sctp_rtoinfo rtoinfo;
        struct sctp_paddrparams paddrparam;
        struct sctp_assocparams assocparams;
index 2b229f7..328cb7c 100644 (file)
@@ -805,6 +805,7 @@ enum sock_flags {
        SOCK_RCU_FREE, /* wait rcu grace period in sk_destruct() */
        SOCK_TXTIME,
        SOCK_XDP, /* XDP is attached */
+       SOCK_TSTAMP_NEW, /* Indicates 64 bit timestamps always */
 };
 
 #define SK_FLAGS_TIMESTAMP ((1UL << SOCK_TIMESTAMP) | (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE))
@@ -1277,7 +1278,7 @@ static inline void sk_sockets_allocated_inc(struct sock *sk)
        percpu_counter_inc(sk->sk_prot->sockets_allocated);
 }
 
-static inline int
+static inline u64
 sk_sockets_allocated_read_positive(struct sock *sk)
 {
        return percpu_counter_read_positive(sk->sk_prot->sockets_allocated);
index 63843ae..5e87b54 100644 (file)
@@ -43,7 +43,6 @@ static inline bool switchdev_trans_ph_commit(struct switchdev_trans *trans)
 
 enum switchdev_attr_id {
        SWITCHDEV_ATTR_ID_UNDEFINED,
-       SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
        SWITCHDEV_ATTR_ID_PORT_STP_STATE,
        SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
        SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT,
@@ -61,7 +60,6 @@ struct switchdev_attr {
        void *complete_priv;
        void (*complete)(struct net_device *dev, int err, void *priv);
        union {
-               struct netdev_phys_item_id ppid;        /* PORT_PARENT_ID */
                u8 stp_state;                           /* PORT_STP_STATE */
                unsigned long brport_flags;             /* PORT_BRIDGE_FLAGS */
                unsigned long brport_flags_support;     /* PORT_BRIDGE_FLAGS_SUPPORT */
@@ -208,9 +206,6 @@ void switchdev_port_fwd_mark_set(struct net_device *dev,
                                 struct net_device *group_dev,
                                 bool joining);
 
-bool switchdev_port_same_parent_id(struct net_device *a,
-                                  struct net_device *b);
-
 int switchdev_handle_port_obj_add(struct net_device *dev,
                        struct switchdev_notifier_port_obj_info *port_obj_info,
                        bool (*check_cb)(const struct net_device *dev),
@@ -295,12 +290,6 @@ call_switchdev_blocking_notifiers(unsigned long val,
        return NOTIFY_DONE;
 }
 
-static inline bool switchdev_port_same_parent_id(struct net_device *a,
-                                                struct net_device *b)
-{
-       return false;
-}
-
 static inline int
 switchdev_handle_port_obj_add(struct net_device *dev,
                        struct switchdev_notifier_port_obj_info *port_obj_info,
index 32d2454..68269e4 100644 (file)
@@ -21,7 +21,7 @@ struct tcf_csum {
 static inline bool is_tcf_csum(const struct tc_action *a)
 {
 #ifdef CONFIG_NET_CLS_ACT
-       if (a->ops && a->ops->type == TCA_ACT_CSUM)
+       if (a->ops && a->ops->id == TCA_ID_CSUM)
                return true;
 #endif
        return false;
index ef8dd0d..ee8d005 100644 (file)
@@ -22,7 +22,7 @@ static inline bool __is_tcf_gact_act(const struct tc_action *a, int act,
 #ifdef CONFIG_NET_CLS_ACT
        struct tcf_gact *gact;
 
-       if (a->ops && a->ops->type != TCA_ACT_GACT)
+       if (a->ops && a->ops->id != TCA_ID_GACT)
                return false;
 
        gact = to_gact(a);
index a2e9cbc..c757585 100644 (file)
@@ -17,7 +17,7 @@ struct tcf_mirred {
 static inline bool is_tcf_mirred_egress_redirect(const struct tc_action *a)
 {
 #ifdef CONFIG_NET_CLS_ACT
-       if (a->ops && a->ops->type == TCA_ACT_MIRRED)
+       if (a->ops && a->ops->id == TCA_ID_MIRRED)
                return to_mirred(a)->tcfm_eaction == TCA_EGRESS_REDIR;
 #endif
        return false;
@@ -26,7 +26,7 @@ static inline bool is_tcf_mirred_egress_redirect(const struct tc_action *a)
 static inline bool is_tcf_mirred_egress_mirror(const struct tc_action *a)
 {
 #ifdef CONFIG_NET_CLS_ACT
-       if (a->ops && a->ops->type == TCA_ACT_MIRRED)
+       if (a->ops && a->ops->id == TCA_ID_MIRRED)
                return to_mirred(a)->tcfm_eaction == TCA_EGRESS_MIRROR;
 #endif
        return false;
index fac3ad4..748cf87 100644 (file)
@@ -23,7 +23,7 @@ struct tcf_pedit {
 static inline bool is_tcf_pedit(const struct tc_action *a)
 {
 #ifdef CONFIG_NET_CLS_ACT
-       if (a->ops && a->ops->type == TCA_ACT_PEDIT)
+       if (a->ops && a->ops->id == TCA_ID_PEDIT)
                return true;
 #endif
        return false;
index 01dbfea..0a559d4 100644 (file)
@@ -20,7 +20,7 @@ struct tcf_sample {
 static inline bool is_tcf_sample(const struct tc_action *a)
 {
 #ifdef CONFIG_NET_CLS_ACT
-       return a->ops && a->ops->type == TCA_ACT_SAMPLE;
+       return a->ops && a->ops->id == TCA_ID_SAMPLE;
 #else
        return false;
 #endif
index 911bbac..85c5c47 100644 (file)
@@ -44,7 +44,7 @@ static inline bool is_tcf_skbedit_mark(const struct tc_action *a)
 #ifdef CONFIG_NET_CLS_ACT
        u32 flags;
 
-       if (a->ops && a->ops->type == TCA_ACT_SKBEDIT) {
+       if (a->ops && a->ops->id == TCA_ID_SKBEDIT) {
                rcu_read_lock();
                flags = rcu_dereference(to_skbedit(a)->params)->flags;
                rcu_read_unlock();
index 46b8c7f..23d5b8b 100644 (file)
@@ -34,7 +34,7 @@ static inline bool is_tcf_tunnel_set(const struct tc_action *a)
        struct tcf_tunnel_key *t = to_tunnel_key(a);
        struct tcf_tunnel_key_params *params = rtnl_dereference(t->params);
 
-       if (a->ops && a->ops->type == TCA_ACT_TUNNEL_KEY)
+       if (a->ops && a->ops->id == TCA_ID_TUNNEL_KEY)
                return params->tcft_action == TCA_TUNNEL_KEY_ACT_SET;
 #endif
        return false;
@@ -46,7 +46,7 @@ static inline bool is_tcf_tunnel_release(const struct tc_action *a)
        struct tcf_tunnel_key *t = to_tunnel_key(a);
        struct tcf_tunnel_key_params *params = rtnl_dereference(t->params);
 
-       if (a->ops && a->ops->type == TCA_ACT_TUNNEL_KEY)
+       if (a->ops && a->ops->id == TCA_ID_TUNNEL_KEY)
                return params->tcft_action == TCA_TUNNEL_KEY_ACT_RELEASE;
 #endif
        return false;
index 22ae260..fe39ed5 100644 (file)
@@ -30,7 +30,7 @@ struct tcf_vlan {
 static inline bool is_tcf_vlan(const struct tc_action *a)
 {
 #ifdef CONFIG_NET_CLS_ACT
-       if (a->ops && a->ops->type == TCA_ACT_VLAN)
+       if (a->ops && a->ops->id == TCA_ID_VLAN)
                return true;
 #endif
        return false;
index 90bf52d..a93a8ed 100644 (file)
@@ -119,7 +119,12 @@ struct tls_rec {
        /* AAD | msg_encrypted.sg.data (data contains overhead for hdr & iv & tag) */
        struct scatterlist sg_aead_out[2];
 
+       char content_type;
+       struct scatterlist sg_content_type;
+
        char aad_space[TLS_AAD_SPACE_SIZE];
+       u8 iv_data[TLS_CIPHER_AES_GCM_128_IV_SIZE +
+                  TLS_CIPHER_AES_GCM_128_SALT_SIZE];
        struct aead_request aead_req;
        u8 aead_req_ctx[];
 };
@@ -137,6 +142,7 @@ struct tls_sw_context_tx {
        struct list_head tx_list;
        atomic_t encrypt_pending;
        int async_notify;
+       int async_capable;
 
 #define BIT_TX_SCHEDULED       0
        unsigned long tx_bitmask;
@@ -200,11 +206,16 @@ struct cipher_context {
        char *iv;
        u16 rec_seq_size;
        char *rec_seq;
+       u16 aad_size;
+       u16 tail_size;
 };
 
 union tls_crypto_context {
        struct tls_crypto_info info;
-       struct tls12_crypto_info_aes_gcm_128 aes_gcm_128;
+       union {
+               struct tls12_crypto_info_aes_gcm_128 aes_gcm_128;
+               struct tls12_crypto_info_aes_gcm_256 aes_gcm_256;
+       };
 };
 
 struct tls_context {
@@ -391,49 +402,77 @@ static inline bool tls_bigint_increment(unsigned char *seq, int len)
 }
 
 static inline void tls_advance_record_sn(struct sock *sk,
-                                        struct cipher_context *ctx)
+                                        struct cipher_context *ctx,
+                                        int version)
 {
        if (tls_bigint_increment(ctx->rec_seq, ctx->rec_seq_size))
                tls_err_abort(sk, EBADMSG);
-       tls_bigint_increment(ctx->iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE,
-                            ctx->iv_size);
+
+       if (version != TLS_1_3_VERSION) {
+               tls_bigint_increment(ctx->iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE,
+                                    ctx->iv_size);
+       }
 }
 
 static inline void tls_fill_prepend(struct tls_context *ctx,
                             char *buf,
                             size_t plaintext_len,
-                            unsigned char record_type)
+                            unsigned char record_type,
+                            int version)
 {
        size_t pkt_len, iv_size = ctx->tx.iv_size;
 
-       pkt_len = plaintext_len + iv_size + ctx->tx.tag_size;
+       pkt_len = plaintext_len + ctx->tx.tag_size;
+       if (version != TLS_1_3_VERSION) {
+               pkt_len += iv_size;
+
+               memcpy(buf + TLS_NONCE_OFFSET,
+                      ctx->tx.iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, iv_size);
+       }
 
        /* we cover nonce explicit here as well, so buf should be of
         * size KTLS_DTLS_HEADER_SIZE + KTLS_DTLS_NONCE_EXPLICIT_SIZE
         */
-       buf[0] = record_type;
-       buf[1] = TLS_VERSION_MINOR(ctx->crypto_send.info.version);
-       buf[2] = TLS_VERSION_MAJOR(ctx->crypto_send.info.version);
+       buf[0] = version == TLS_1_3_VERSION ?
+                  TLS_RECORD_TYPE_DATA : record_type;
+       /* Note that VERSION must be TLS_1_2 for both TLS1.2 and TLS1.3 */
+       buf[1] = TLS_1_2_VERSION_MINOR;
+       buf[2] = TLS_1_2_VERSION_MAJOR;
        /* we can use IV for nonce explicit according to spec */
        buf[3] = pkt_len >> 8;
        buf[4] = pkt_len & 0xFF;
-       memcpy(buf + TLS_NONCE_OFFSET,
-              ctx->tx.iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, iv_size);
 }
 
 static inline void tls_make_aad(char *buf,
                                size_t size,
                                char *record_sequence,
                                int record_sequence_size,
-                               unsigned char record_type)
+                               unsigned char record_type,
+                               int version)
 {
-       memcpy(buf, record_sequence, record_sequence_size);
+       if (version != TLS_1_3_VERSION) {
+               memcpy(buf, record_sequence, record_sequence_size);
+               buf += 8;
+       } else {
+               size += TLS_CIPHER_AES_GCM_128_TAG_SIZE;
+       }
 
-       buf[8] = record_type;
-       buf[9] = TLS_1_2_VERSION_MAJOR;
-       buf[10] = TLS_1_2_VERSION_MINOR;
-       buf[11] = size >> 8;
-       buf[12] = size & 0xFF;
+       buf[0] = version == TLS_1_3_VERSION ?
+                 TLS_RECORD_TYPE_DATA : record_type;
+       buf[1] = TLS_1_2_VERSION_MAJOR;
+       buf[2] = TLS_1_2_VERSION_MINOR;
+       buf[3] = size >> 8;
+       buf[4] = size & 0xFF;
+}
+
+static inline void xor_iv_with_seq(int version, char *iv, char *seq)
+{
+       int i;
+
+       if (version == TLS_1_3_VERSION) {
+               for (i = 0; i < 8; i++)
+                       iv[i + 4] ^= seq[i];
+       }
 }
 
 static inline struct tls_context *tls_get_ctx(const struct sock *sk)
index a3ceed3..80debf5 100644 (file)
@@ -2579,9 +2579,10 @@ struct ib_device {
 
        const struct uapi_definition   *driver_def;
        enum rdma_driver_id             driver_id;
+
        /*
-        * Provides synchronization between device unregistration and netlink
-        * commands on a device. To be used only by core.
+        * Positive refcount indicates that the device is currently
+        * registered and cannot be unregistered.
         */
        refcount_t refcount;
        struct completion unreg_completion;
@@ -3926,6 +3927,25 @@ static inline bool ib_access_writable(int access_flags)
 int ib_check_mr_status(struct ib_mr *mr, u32 check_mask,
                       struct ib_mr_status *mr_status);
 
+/**
+ * ib_device_try_get: Hold a registration lock
+ * device: The device to lock
+ *
+ * A device under an active registration lock cannot become unregistered. It
+ * is only possible to obtain a registration lock on a device that is fully
+ * registered, otherwise this function returns false.
+ *
+ * The registration lock is only necessary for actions which require the
+ * device to still be registered. Uses that only require the device pointer to
+ * be valid should use get_device(&ibdev->dev) to hold the memory.
+ *
+ */
+static inline bool ib_device_try_get(struct ib_device *dev)
+{
+       return refcount_inc_not_zero(&dev->refcount);
+}
+
+void ib_device_put(struct ib_device *device);
 struct net_device *ib_get_net_dev_by_params(struct ib_device *dev, u8 port,
                                            u16 pkey, const union ib_gid *gid,
                                            const struct sockaddr *addr);
index 0cdc399..c5188ff 100644 (file)
@@ -173,7 +173,11 @@ static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
        if (snd_BUG_ON(!stream))
                return;
 
-       stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+       if (stream->direction == SND_COMPRESS_PLAYBACK)
+               stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+       else
+               stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
+
        wake_up(&stream->runtime->sleep);
 }
 
index 7fa48b1..cc7c8d4 100644 (file)
@@ -68,6 +68,7 @@ struct hda_bus {
        unsigned int response_reset:1;  /* controller was reset */
        unsigned int in_reset:1;        /* during reset operation */
        unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
+       unsigned int bus_probing :1;    /* during probing process */
 
        int primary_dig_out_type;       /* primary digital out PCM type */
        unsigned int mixer_assigned;    /* codec addr for mixer name */
index 44acfbc..191ddf6 100644 (file)
@@ -46,6 +46,100 @@ TRACE_EVENT(devlink_hwmsg,
                  (int) __entry->len, __get_dynamic_array(buf), __entry->len)
 );
 
+/*
+ * Tracepoint for devlink hardware error:
+ */
+TRACE_EVENT(devlink_hwerr,
+       TP_PROTO(const struct devlink *devlink, int err, const char *msg),
+
+       TP_ARGS(devlink, err, msg),
+
+       TP_STRUCT__entry(
+               __string(bus_name, devlink->dev->bus->name)
+               __string(dev_name, dev_name(devlink->dev))
+               __string(driver_name, devlink->dev->driver->name)
+               __field(int, err)
+               __string(msg, msg)
+               ),
+
+       TP_fast_assign(
+               __assign_str(bus_name, devlink->dev->bus->name);
+               __assign_str(dev_name, dev_name(devlink->dev));
+               __assign_str(driver_name, devlink->dev->driver->name);
+               __entry->err = err;
+               __assign_str(msg, msg);
+               ),
+
+       TP_printk("bus_name=%s dev_name=%s driver_name=%s err=%d %s",
+                       __get_str(bus_name), __get_str(dev_name),
+                       __get_str(driver_name), __entry->err, __get_str(msg))
+);
+
+/*
+ * Tracepoint for devlink health message:
+ */
+TRACE_EVENT(devlink_health_report,
+       TP_PROTO(const struct devlink *devlink, const char *reporter_name,
+                const char *msg),
+
+       TP_ARGS(devlink, reporter_name, msg),
+
+       TP_STRUCT__entry(
+               __string(bus_name, devlink->dev->bus->name)
+               __string(dev_name, dev_name(devlink->dev))
+               __string(driver_name, devlink->dev->driver->name)
+               __string(reporter_name, msg)
+               __string(msg, msg)
+       ),
+
+       TP_fast_assign(
+               __assign_str(bus_name, devlink->dev->bus->name);
+               __assign_str(dev_name, dev_name(devlink->dev));
+               __assign_str(driver_name, devlink->dev->driver->name);
+               __assign_str(reporter_name, reporter_name);
+               __assign_str(msg, msg);
+       ),
+
+       TP_printk("bus_name=%s dev_name=%s driver_name=%s reporter_name=%s: %s",
+                 __get_str(bus_name), __get_str(dev_name),
+                 __get_str(driver_name), __get_str(reporter_name),
+                 __get_str(msg))
+);
+
+/*
+ * Tracepoint for devlink health recover aborted message:
+ */
+TRACE_EVENT(devlink_health_recover_aborted,
+       TP_PROTO(const struct devlink *devlink, const char *reporter_name,
+                bool health_state, u64 time_since_last_recover),
+
+       TP_ARGS(devlink, reporter_name, health_state, time_since_last_recover),
+
+       TP_STRUCT__entry(
+               __string(bus_name, devlink->dev->bus->name)
+               __string(dev_name, dev_name(devlink->dev))
+               __string(driver_name, devlink->dev->driver->name)
+               __string(reporter_name, reporter_name)
+               __field(bool, health_state)
+               __field(u64, time_since_last_recover)
+       ),
+
+       TP_fast_assign(
+               __assign_str(bus_name, devlink->dev->bus->name);
+               __assign_str(dev_name, dev_name(devlink->dev));
+               __assign_str(driver_name, devlink->dev->driver->name);
+               __assign_str(reporter_name, reporter_name);
+               __entry->health_state = health_state;
+               __entry->time_since_last_recover = time_since_last_recover;
+       ),
+
+       TP_printk("bus_name=%s dev_name=%s driver_name=%s reporter_name=%s: health_state=%d time_since_last_recover=%llu recover aborted",
+                 __get_str(bus_name), __get_str(dev_name),
+                 __get_str(driver_name), __get_str(reporter_name),
+                 __entry->health_state,
+                 __entry->time_since_last_recover)
+);
+
 #endif /* _TRACE_DEVLINK_H */
 
 /* This part must be outside protection */
@@ -64,6 +158,10 @@ static inline void trace_devlink_hwmsg(const struct devlink *devlink,
 {
 }
 
+static inline void trace_devlink_hwerr(const struct devlink *devlink,
+                                      int err, const char *msg)
+{
+}
 #endif /* _TRACE_DEVLINK_H */
 
 #endif
diff --git a/include/trace/events/mlxsw.h b/include/trace/events/mlxsw.h
new file mode 100644 (file)
index 0000000..a5ce6df
--- /dev/null
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/* Copyright (c) 2019 Mellanox Technologies. All rights reserved */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mlxsw
+
+#if !defined(_MLXSW_TRACEPOINT_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _MLXSW_TRACEPOINT_H
+
+#include <linux/tracepoint.h>
+
+struct mlxsw_sp;
+struct mlxsw_sp_acl_atcam_region;
+struct mlxsw_sp_acl_tcam_vregion;
+
+TRACE_EVENT(mlxsw_sp_acl_atcam_entry_add_ctcam_spill,
+       TP_PROTO(const struct mlxsw_sp *mlxsw_sp,
+                const struct mlxsw_sp_acl_atcam_region *aregion),
+
+       TP_ARGS(mlxsw_sp, aregion),
+
+       TP_STRUCT__entry(
+               __field(const void *, mlxsw_sp)
+               __field(const void *, aregion)
+       ),
+
+       TP_fast_assign(
+               __entry->mlxsw_sp = mlxsw_sp;
+               __entry->aregion = aregion;
+       ),
+
+       TP_printk("mlxsw_sp %p, aregion %p",
+                 __entry->mlxsw_sp, __entry->aregion)
+);
+
+TRACE_EVENT(mlxsw_sp_acl_tcam_vregion_rehash,
+       TP_PROTO(const struct mlxsw_sp *mlxsw_sp,
+                const struct mlxsw_sp_acl_tcam_vregion *vregion),
+
+       TP_ARGS(mlxsw_sp, vregion),
+
+       TP_STRUCT__entry(
+               __field(const void *, mlxsw_sp)
+               __field(const void *, vregion)
+       ),
+
+       TP_fast_assign(
+               __entry->mlxsw_sp = mlxsw_sp;
+               __entry->vregion = vregion;
+       ),
+
+       TP_printk("mlxsw_sp %p, vregion %p",
+                 __entry->mlxsw_sp, __entry->vregion)
+);
+
+TRACE_EVENT(mlxsw_sp_acl_tcam_vregion_migrate,
+       TP_PROTO(const struct mlxsw_sp *mlxsw_sp,
+                const struct mlxsw_sp_acl_tcam_vregion *vregion),
+
+       TP_ARGS(mlxsw_sp, vregion),
+
+       TP_STRUCT__entry(
+               __field(const void *, mlxsw_sp)
+               __field(const void *, vregion)
+       ),
+
+       TP_fast_assign(
+               __entry->mlxsw_sp = mlxsw_sp;
+               __entry->vregion = vregion;
+       ),
+
+       TP_printk("mlxsw_sp %p, vregion %p",
+                 __entry->mlxsw_sp, __entry->vregion)
+);
+
+TRACE_EVENT(mlxsw_sp_acl_tcam_vregion_rehash_dis,
+       TP_PROTO(const struct mlxsw_sp *mlxsw_sp,
+                const struct mlxsw_sp_acl_tcam_vregion *vregion),
+
+       TP_ARGS(mlxsw_sp, vregion),
+
+       TP_STRUCT__entry(
+               __field(const void *, mlxsw_sp)
+               __field(const void *, vregion)
+       ),
+
+       TP_fast_assign(
+               __entry->mlxsw_sp = mlxsw_sp;
+               __entry->vregion = vregion;
+       ),
+
+       TP_printk("mlxsw_sp %p, vregion %p",
+                 __entry->mlxsw_sp, __entry->vregion)
+);
+
+#endif /* _MLXSW_TRACEPOINT_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/neigh.h b/include/trace/events/neigh.h
new file mode 100644 (file)
index 0000000..ed10353
--- /dev/null
@@ -0,0 +1,204 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM neigh
+
+#if !defined(_TRACE_NEIGH_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_NEIGH_H
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/tracepoint.h>
+#include <net/neighbour.h>
+
+#define neigh_state_str(state)                         \
+       __print_symbolic(state,                         \
+               { NUD_INCOMPLETE, "incomplete" },       \
+               { NUD_REACHABLE, "reachable" },         \
+               { NUD_STALE, "stale" },                 \
+               { NUD_DELAY, "delay" },                 \
+               { NUD_PROBE, "probe" },                 \
+               { NUD_FAILED, "failed" })
+
+TRACE_EVENT(neigh_update,
+
+       TP_PROTO(struct neighbour *n, const u8 *lladdr, u8 new,
+                u32 flags, u32 nlmsg_pid),
+
+       TP_ARGS(n, lladdr, new, flags, nlmsg_pid),
+
+       TP_STRUCT__entry(
+               __field(u32, family)
+               __string(dev, (n->dev ? n->dev->name : "NULL"))
+               __array(u8, lladdr, MAX_ADDR_LEN)
+               __field(u8, lladdr_len)
+               __field(u8, flags)
+               __field(u8, nud_state)
+               __field(u8, type)
+               __field(u8, dead)
+               __field(int, refcnt)
+               __array(__u8, primary_key4, 4)
+               __array(__u8, primary_key6, 16)
+               __field(unsigned long, confirmed)
+               __field(unsigned long, updated)
+               __field(unsigned long, used)
+               __array(u8, new_lladdr, MAX_ADDR_LEN)
+               __field(u8, new_state)
+               __field(u32, update_flags)
+               __field(u32, pid)
+       ),
+
+       TP_fast_assign(
+               int lladdr_len = (n->dev ? n->dev->addr_len : MAX_ADDR_LEN);
+               struct in6_addr *pin6;
+               __be32 *p32;
+
+               __entry->family = n->tbl->family;
+               __assign_str(dev, (n->dev ? n->dev->name : "NULL"));
+               __entry->lladdr_len = lladdr_len;
+               memcpy(__entry->lladdr, n->ha, lladdr_len);
+               __entry->flags = n->flags;
+               __entry->nud_state = n->nud_state;
+               __entry->type = n->type;
+               __entry->dead = n->dead;
+               __entry->refcnt = refcount_read(&n->refcnt);
+               pin6 = (struct in6_addr *)__entry->primary_key6;
+               p32 = (__be32 *)__entry->primary_key4;
+
+               if (n->tbl->family == AF_INET)
+                       *p32 = *(__be32 *)n->primary_key;
+               else
+                       *p32 = 0;
+
+#if IS_ENABLED(CONFIG_IPV6)
+               if (n->tbl->family == AF_INET6) {
+                       pin6 = (struct in6_addr *)__entry->primary_key6;
+                       *pin6 = *(struct in6_addr *)n->primary_key;
+               } else
+#endif
+               {
+                       ipv6_addr_set_v4mapped(*p32, pin6);
+               }
+               __entry->confirmed = n->confirmed;
+               __entry->updated = n->updated;
+               __entry->used = n->used;
+               if (lladdr)
+                       memcpy(__entry->new_lladdr, lladdr, lladdr_len);
+               __entry->new_state = new;
+               __entry->update_flags = flags;
+               __entry->pid = nlmsg_pid;
+       ),
+
+       TP_printk("family %d dev %s lladdr %s flags %02x nud_state %s type %02x "
+                 "dead %d refcnt %d primary_key4 %pI4 primary_key6 %pI6c "
+                 "confirmed %lu updated %lu used %lu new_lladdr %s "
+                 "new_state %02x update_flags %02x pid %d",
+                 __entry->family, __get_str(dev),
+                 __print_hex_str(__entry->lladdr, __entry->lladdr_len),
+                 __entry->flags, neigh_state_str(__entry->nud_state),
+                 __entry->type, __entry->dead, __entry->refcnt,
+                 __entry->primary_key4, __entry->primary_key6,
+                 __entry->confirmed, __entry->updated, __entry->used,
+                 __print_hex_str(__entry->new_lladdr, __entry->lladdr_len),
+                 __entry->new_state,
+                 __entry->update_flags, __entry->pid)
+);
+
+DECLARE_EVENT_CLASS(neigh__update,
+       TP_PROTO(struct neighbour *n, int err),
+       TP_ARGS(n, err),
+       TP_STRUCT__entry(
+               __field(u32, family)
+               __string(dev, (n->dev ? n->dev->name : "NULL"))
+               __array(u8, lladdr, MAX_ADDR_LEN)
+               __field(u8, lladdr_len)
+               __field(u8, flags)
+               __field(u8, nud_state)
+               __field(u8, type)
+               __field(u8, dead)
+               __field(int, refcnt)
+               __array(__u8, primary_key4, 4)
+               __array(__u8, primary_key6, 16)
+               __field(unsigned long, confirmed)
+               __field(unsigned long, updated)
+               __field(unsigned long, used)
+               __field(u32, err)
+       ),
+
+       TP_fast_assign(
+               int lladdr_len = (n->dev ? n->dev->addr_len : MAX_ADDR_LEN);
+               struct in6_addr *pin6;
+               __be32 *p32;
+
+               __entry->family = n->tbl->family;
+               __assign_str(dev, (n->dev ? n->dev->name : "NULL"));
+               __entry->lladdr_len = lladdr_len;
+               memcpy(__entry->lladdr, n->ha, lladdr_len);
+               __entry->flags = n->flags;
+               __entry->nud_state = n->nud_state;
+               __entry->type = n->type;
+               __entry->dead = n->dead;
+               __entry->refcnt = refcount_read(&n->refcnt);
+               pin6 = (struct in6_addr *)__entry->primary_key6;
+               p32 = (__be32 *)__entry->primary_key4;
+
+               if (n->tbl->family == AF_INET)
+                       *p32 = *(__be32 *)n->primary_key;
+               else
+                       *p32 = 0;
+
+#if IS_ENABLED(CONFIG_IPV6)
+               if (n->tbl->family == AF_INET6) {
+                       pin6 = (struct in6_addr *)__entry->primary_key6;
+                       *pin6 = *(struct in6_addr *)n->primary_key;
+               } else
+#endif
+               {
+                       ipv6_addr_set_v4mapped(*p32, pin6);
+               }
+
+               __entry->confirmed = n->confirmed;
+               __entry->updated = n->updated;
+               __entry->used = n->used;
+               __entry->err = err;
+       ),
+
+       TP_printk("family %d dev %s lladdr %s flags %02x nud_state %s type %02x "
+                 "dead %d refcnt %d primary_key4 %pI4 primary_key6 %pI6c "
+                 "confirmed %lu updated %lu used %lu err %d",
+                 __entry->family, __get_str(dev),
+                 __print_hex_str(__entry->lladdr, __entry->lladdr_len),
+                 __entry->flags, neigh_state_str(__entry->nud_state),
+                 __entry->type, __entry->dead, __entry->refcnt,
+                 __entry->primary_key4, __entry->primary_key6,
+                 __entry->confirmed, __entry->updated, __entry->used,
+                 __entry->err)
+);
+
+DEFINE_EVENT(neigh__update, neigh_update_done,
+       TP_PROTO(struct neighbour *neigh, int err),
+       TP_ARGS(neigh, err)
+);
+
+DEFINE_EVENT(neigh__update, neigh_timer_handler,
+       TP_PROTO(struct neighbour *neigh, int err),
+       TP_ARGS(neigh, err)
+);
+
+DEFINE_EVENT(neigh__update, neigh_event_send_done,
+       TP_PROTO(struct neighbour *neigh, int err),
+       TP_ARGS(neigh, err)
+);
+
+DEFINE_EVENT(neigh__update, neigh_event_send_dead,
+       TP_PROTO(struct neighbour *neigh, int err),
+       TP_ARGS(neigh, err)
+);
+
+DEFINE_EVENT(neigh__update, neigh_cleanup_and_release,
+       TP_PROTO(struct neighbour *neigh, int rc),
+       TP_ARGS(neigh, rc)
+);
+
+#endif /* _TRACE_NEIGH_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 3066ab3..c8b430c 100644 (file)
@@ -3,6 +3,7 @@
 #define __ASM_GENERIC_SOCKET_H
 
 #include <asm/sockios.h>
+#include <asm/bitsperlong.h>
 
 /* For setsockopt(2) */
 #define SOL_SOCKET     1
@@ -29,8 +30,8 @@
 #define SO_PEERCRED    17
 #define SO_RCVLOWAT    18
 #define SO_SNDLOWAT    19
-#define SO_RCVTIMEO    20
-#define SO_SNDTIMEO    21
+#define SO_RCVTIMEO_OLD        20
+#define SO_SNDTIMEO_OLD        21
 #endif
 
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_GET_FILTER          SO_ATTACH_FILTER
 
 #define SO_PEERNAME            28
-#define SO_TIMESTAMP           29
-#define SCM_TIMESTAMP          SO_TIMESTAMP
 
 #define SO_ACCEPTCONN          30
 
 #define SO_PEERSEC             31
 #define SO_PASSSEC             34
-#define SO_TIMESTAMPNS         35
-#define SCM_TIMESTAMPNS                SO_TIMESTAMPNS
 
 #define SO_MARK                        36
 
-#define SO_TIMESTAMPING                37
-#define SCM_TIMESTAMPING       SO_TIMESTAMPING
-
 #define SO_PROTOCOL            38
 #define SO_DOMAIN              39
 
 
 #define SO_BINDTOIFINDEX       62
 
+#define SO_TIMESTAMP_OLD        29
+#define SO_TIMESTAMPNS_OLD      35
+#define SO_TIMESTAMPING_OLD     37
+
+#define SO_TIMESTAMP_NEW        63
+#define SO_TIMESTAMPNS_NEW      64
+#define SO_TIMESTAMPING_NEW     65
+
+#define SO_RCVTIMEO_NEW         66
+#define SO_SNDTIMEO_NEW         67
+
+#if !defined(__KERNEL__)
+
+#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
+/* on 64-bit and x32, avoid the ?: operator */
+#define SO_TIMESTAMP           SO_TIMESTAMP_OLD
+#define SO_TIMESTAMPNS         SO_TIMESTAMPNS_OLD
+#define SO_TIMESTAMPING                SO_TIMESTAMPING_OLD
+
+#define SO_RCVTIMEO            SO_RCVTIMEO_OLD
+#define SO_SNDTIMEO            SO_SNDTIMEO_OLD
+#else
+#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
+#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW)
+#define SO_TIMESTAMPING (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPING_OLD : SO_TIMESTAMPING_NEW)
+
+#define SO_RCVTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_RCVTIMEO_OLD : SO_RCVTIMEO_NEW)
+#define SO_SNDTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_SNDTIMEO_OLD : SO_SNDTIMEO_NEW)
+#endif
+
+#define SCM_TIMESTAMP           SO_TIMESTAMP
+#define SCM_TIMESTAMPNS         SO_TIMESTAMPNS
+#define SCM_TIMESTAMPING        SO_TIMESTAMPING
+
+#endif
+
 #endif /* __ASM_GENERIC_SOCKET_H */
index 894d8d2..c99336f 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
@@ -229,7 +229,7 @@ struct batadv_ogm_packet {
  * @packet_type: batman-adv packet type, part of the general header
  * @version: batman-adv protocol version, part of the general header
  * @ttl: time to live for this packet, part of the general header
- * @flags: reseved for routing relevant flags - currently always 0
+ * @flags: reserved for routing relevant flags - currently always 0
  * @seqno: sequence number
  * @orig: originator mac address
  * @tvlv_len: length of the appended tvlv buffer (in bytes)
index 324a0e1..305bf31 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: MIT */
-/* Copyright (C) 2016-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2016-2019  B.A.T.M.A.N. contributors:
  *
  * Matthias Schiffer
  *
@@ -27,6 +27,7 @@
 
 #define BATADV_NL_NAME "batadv"
 
+#define BATADV_NL_MCAST_GROUP_CONFIG   "config"
 #define BATADV_NL_MCAST_GROUP_TPMETER  "tpmeter"
 
 /**
@@ -139,6 +140,20 @@ enum batadv_mcast_flags_priv {
 };
 
 /**
+ * enum batadv_gw_modes - gateway mode of node
+ */
+enum batadv_gw_modes {
+       /** @BATADV_GW_MODE_OFF: gw mode disabled */
+       BATADV_GW_MODE_OFF,
+
+       /** @BATADV_GW_MODE_CLIENT: send DHCP requests to gw servers */
+       BATADV_GW_MODE_CLIENT,
+
+       /** @BATADV_GW_MODE_SERVER: announce itself as gatway server */
+       BATADV_GW_MODE_SERVER,
+};
+
+/**
  * enum batadv_nl_attrs - batman-adv netlink attributes
  */
 enum batadv_nl_attrs {
@@ -344,6 +359,138 @@ enum batadv_nl_attrs {
         */
        BATADV_ATTR_MCAST_FLAGS_PRIV,
 
+       /**
+        * @BATADV_ATTR_VLANID: VLAN id on top of soft interface
+        */
+       BATADV_ATTR_VLANID,
+
+       /**
+        * @BATADV_ATTR_AGGREGATED_OGMS_ENABLED: whether the batman protocol
+        *  messages of the mesh interface shall be aggregated or not.
+        */
+       BATADV_ATTR_AGGREGATED_OGMS_ENABLED,
+
+       /**
+        * @BATADV_ATTR_AP_ISOLATION_ENABLED: whether the data traffic going
+        *  from a wireless client to another wireless client will be silently
+        *  dropped.
+        */
+       BATADV_ATTR_AP_ISOLATION_ENABLED,
+
+       /**
+        * @BATADV_ATTR_ISOLATION_MARK: the isolation mark which is used to
+        *  classify clients as "isolated" by the Extended Isolation feature.
+        */
+       BATADV_ATTR_ISOLATION_MARK,
+
+       /**
+        * @BATADV_ATTR_ISOLATION_MASK: the isolation (bit)mask which is used to
+        *  classify clients as "isolated" by the Extended Isolation feature.
+        */
+       BATADV_ATTR_ISOLATION_MASK,
+
+       /**
+        * @BATADV_ATTR_BONDING_ENABLED: whether the data traffic going through
+        *  the mesh will be sent using multiple interfaces at the same time.
+        */
+       BATADV_ATTR_BONDING_ENABLED,
+
+       /**
+        * @BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED: whether the bridge loop
+        *  avoidance feature is enabled. This feature detects and avoids loops
+        *  between the mesh and devices bridged with the soft interface
+        */
+       BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED,
+
+       /**
+        * @BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED: whether the distributed
+        *  arp table feature is enabled. This feature uses a distributed hash
+        *  table to answer ARP requests without flooding the request through
+        *  the whole mesh.
+        */
+       BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED,
+
+       /**
+        * @BATADV_ATTR_FRAGMENTATION_ENABLED: whether the data traffic going
+        *  through the mesh will be fragmented or silently discarded if the
+        *  packet size exceeds the outgoing interface MTU.
+        */
+       BATADV_ATTR_FRAGMENTATION_ENABLED,
+
+       /**
+        * @BATADV_ATTR_GW_BANDWIDTH_DOWN: defines the download bandwidth which
+        *  is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
+        *  to 'server'.
+        */
+       BATADV_ATTR_GW_BANDWIDTH_DOWN,
+
+       /**
+        * @BATADV_ATTR_GW_BANDWIDTH_UP: defines the upload bandwidth which
+        *  is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
+        *  to 'server'.
+        */
+       BATADV_ATTR_GW_BANDWIDTH_UP,
+
+       /**
+        * @BATADV_ATTR_GW_MODE: defines the state of the gateway features.
+        * Possible values are specified in enum batadv_gw_modes
+        */
+       BATADV_ATTR_GW_MODE,
+
+       /**
+        * @BATADV_ATTR_GW_SEL_CLASS: defines the selection criteria this node
+        *  will use to choose a gateway if gw_mode was set to 'client'.
+        */
+       BATADV_ATTR_GW_SEL_CLASS,
+
+       /**
+        * @BATADV_ATTR_HOP_PENALTY: defines the penalty which will be applied
+        *  to an originator message's tq-field on every hop.
+        */
+       BATADV_ATTR_HOP_PENALTY,
+
+       /**
+        * @BATADV_ATTR_LOG_LEVEL: bitmask with to define which debug messages
+        *  should be send to the debug log/trace ring buffer
+        */
+       BATADV_ATTR_LOG_LEVEL,
+
+       /**
+        * @BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED: whether multicast
+        *  optimizations should be replaced by simple broadcast-like flooding
+        *  of multicast packets. If set to non-zero then all nodes in the mesh
+        *  are going to use classic flooding for any multicast packet with no
+        *  optimizations.
+        */
+       BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED,
+
+       /**
+        * @BATADV_ATTR_NETWORK_CODING_ENABLED: whether Network Coding (using
+        *  some magic to send fewer wifi packets but still the same content) is
+        *  enabled or not.
+        */
+       BATADV_ATTR_NETWORK_CODING_ENABLED,
+
+       /**
+        * @BATADV_ATTR_ORIG_INTERVAL: defines the interval in milliseconds in
+        *  which batman sends its protocol messages.
+        */
+       BATADV_ATTR_ORIG_INTERVAL,
+
+       /**
+        * @BATADV_ATTR_ELP_INTERVAL: defines the interval in milliseconds in
+        *  which batman emits probing packets for neighbor sensing (ELP).
+        */
+       BATADV_ATTR_ELP_INTERVAL,
+
+       /**
+        * @BATADV_ATTR_THROUGHPUT_OVERRIDE: defines the throughput value to be
+        *  used by B.A.T.M.A.N. V when estimating the link throughput using
+        *  this interface. If the value is set to 0 then batman-adv will try to
+        *  estimate the throughput by itself.
+        */
+       BATADV_ATTR_THROUGHPUT_OVERRIDE,
+
        /* add attributes above here, update the policy in netlink.c */
 
        /**
@@ -372,10 +519,14 @@ enum batadv_nl_commands {
        BATADV_CMD_UNSPEC,
 
        /**
-        * @BATADV_CMD_GET_MESH_INFO: Query basic information about batman-adv
-        * device
+        * @BATADV_CMD_GET_MESH: Get attributes from softif/mesh
+        */
+       BATADV_CMD_GET_MESH,
+
+       /**
+        * @BATADV_CMD_GET_MESH_INFO: Alias for @BATADV_CMD_GET_MESH
         */
-       BATADV_CMD_GET_MESH_INFO,
+       BATADV_CMD_GET_MESH_INFO = BATADV_CMD_GET_MESH,
 
        /**
         * @BATADV_CMD_TP_METER: Start a tp meter session
@@ -393,9 +544,15 @@ enum batadv_nl_commands {
        BATADV_CMD_GET_ROUTING_ALGOS,
 
        /**
-        * @BATADV_CMD_GET_HARDIFS: Query list of hard interfaces
+        * @BATADV_CMD_GET_HARDIF: Get attributes from a hardif of the
+        *  current softif
         */
-       BATADV_CMD_GET_HARDIFS,
+       BATADV_CMD_GET_HARDIF,
+
+       /**
+        * @BATADV_CMD_GET_HARDIFS: Alias for @BATADV_CMD_GET_HARDIF
+        */
+       BATADV_CMD_GET_HARDIFS = BATADV_CMD_GET_HARDIF,
 
        /**
         * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations
@@ -443,6 +600,29 @@ enum batadv_nl_commands {
         */
        BATADV_CMD_GET_MCAST_FLAGS,
 
+       /**
+        * @BATADV_CMD_SET_MESH: Set attributes for softif/mesh
+        */
+       BATADV_CMD_SET_MESH,
+
+       /**
+        * @BATADV_CMD_SET_HARDIF: Set attributes for hardif of the
+        *  current softif
+        */
+       BATADV_CMD_SET_HARDIF,
+
+       /**
+        * @BATADV_CMD_GET_VLAN: Get attributes from a VLAN of the
+        *  current softif
+        */
+       BATADV_CMD_GET_VLAN,
+
+       /**
+        * @BATADV_CMD_SET_VLAN: Set attributes for VLAN of the
+        *  current softif
+        */
+       BATADV_CMD_SET_VLAN,
+
        /* add new commands above here */
 
        /**
index 60b99b7..bcdd247 100644 (file)
@@ -267,6 +267,7 @@ enum bpf_attach_type {
 #define BPF_ANY                0 /* create new element or update existing */
 #define BPF_NOEXIST    1 /* create new element if it didn't exist */
 #define BPF_EXIST      2 /* update existing element */
+#define BPF_F_LOCK     4 /* spin_lock-ed map_lookup/map_update */
 
 /* flags for BPF_MAP_CREATE command */
 #define BPF_F_NO_PREALLOC      (1U << 0)
@@ -2015,6 +2016,19 @@ union bpf_attr {
  *                     Only works if *skb* contains an IPv6 packet. Insert a
  *                     Segment Routing Header (**struct ipv6_sr_hdr**) inside
  *                     the IPv6 header.
+ *             **BPF_LWT_ENCAP_IP**
+ *                     IP encapsulation (GRE/GUE/IPIP/etc). The outer header
+ *                     must be IPv4 or IPv6, followed by zero or more
+ *                     additional headers, up to LWT_BPF_MAX_HEADROOM total
+ *                     bytes in all prepended headers. Please note that
+ *                     if skb_is_gso(skb) is true, no more than two headers
+ *                     can be prepended, and the inner header, if present,
+ *                     should be either GRE or UDP/GUE.
+ *
+ *             BPF_LWT_ENCAP_SEG6*** types can be called by bpf programs of
+ *             type BPF_PROG_TYPE_LWT_IN; BPF_LWT_ENCAP_IP type can be called
+ *             by bpf programs of types BPF_PROG_TYPE_LWT_IN and
+ *             BPF_PROG_TYPE_LWT_XMIT.
  *
  *             A call to this helper is susceptible to change the underlaying
  *             packet buffer. Therefore, at load time, all checks on pointers
@@ -2328,6 +2342,23 @@ union bpf_attr {
  *             "**y**".
  *     Return
  *             0
+ *
+ * struct bpf_sock *bpf_sk_fullsock(struct bpf_sock *sk)
+ *     Description
+ *             This helper gets a **struct bpf_sock** pointer such
+ *             that all the fields in bpf_sock can be accessed.
+ *     Return
+ *             A **struct bpf_sock** pointer on success, or NULL in
+ *             case of failure.
+ *
+ * struct bpf_tcp_sock *bpf_tcp_sock(struct bpf_sock *sk)
+ *     Description
+ *             This helper gets a **struct bpf_tcp_sock** pointer from a
+ *             **struct bpf_sock** pointer.
+ *
+ *     Return
+ *             A **struct bpf_tcp_sock** pointer on success, or NULL in
+ *             case of failure.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -2422,7 +2453,11 @@ union bpf_attr {
        FN(map_peek_elem),              \
        FN(msg_push_data),              \
        FN(msg_pop_data),               \
-       FN(rc_pointer_rel),
+       FN(rc_pointer_rel),             \
+       FN(spin_lock),                  \
+       FN(spin_unlock),                \
+       FN(sk_fullsock),                \
+       FN(tcp_sock),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -2495,7 +2530,8 @@ enum bpf_hdr_start_off {
 /* Encapsulation type for BPF_FUNC_lwt_push_encap helper. */
 enum bpf_lwt_encap_mode {
        BPF_LWT_ENCAP_SEG6,
-       BPF_LWT_ENCAP_SEG6_INLINE
+       BPF_LWT_ENCAP_SEG6_INLINE,
+       BPF_LWT_ENCAP_IP,
 };
 
 #define __bpf_md_ptr(type, name)       \
@@ -2542,6 +2578,7 @@ struct __sk_buff {
        __u64 tstamp;
        __u32 wire_len;
        __u32 gso_segs;
+       __bpf_md_ptr(struct bpf_sock *, sk);
 };
 
 struct bpf_tunnel_key {
@@ -2583,7 +2620,15 @@ enum bpf_ret_code {
        BPF_DROP = 2,
        /* 3-6 reserved */
        BPF_REDIRECT = 7,
-       /* >127 are reserved for prog type specific return codes */
+       /* >127 are reserved for prog type specific return codes.
+        *
+        * BPF_LWT_REROUTE: used by BPF_PROG_TYPE_LWT_IN and
+        *    BPF_PROG_TYPE_LWT_XMIT to indicate that skb had been
+        *    changed and should be routed based on its new L3 header.
+        *    (This is an L3 redirect, as opposed to L2 redirect
+        *    represented by BPF_REDIRECT above).
+        */
+       BPF_LWT_REROUTE = 128,
 };
 
 struct bpf_sock {
@@ -2593,14 +2638,52 @@ struct bpf_sock {
        __u32 protocol;
        __u32 mark;
        __u32 priority;
-       __u32 src_ip4;          /* Allows 1,2,4-byte read.
-                                * Stored in network byte order.
+       /* IP address also allows 1 and 2 bytes access */
+       __u32 src_ip4;
+       __u32 src_ip6[4];
+       __u32 src_port;         /* host byte order */
+       __u32 dst_port;         /* network byte order */
+       __u32 dst_ip4;
+       __u32 dst_ip6[4];
+       __u32 state;
+};
+
+struct bpf_tcp_sock {
+       __u32 snd_cwnd;         /* Sending congestion window            */
+       __u32 srtt_us;          /* smoothed round trip time << 3 in usecs */
+       __u32 rtt_min;
+       __u32 snd_ssthresh;     /* Slow start size threshold            */
+       __u32 rcv_nxt;          /* What we want to receive next         */
+       __u32 snd_nxt;          /* Next sequence we send                */
+       __u32 snd_una;          /* First byte we want an ack for        */
+       __u32 mss_cache;        /* Cached effective mss, not including SACKS */
+       __u32 ecn_flags;        /* ECN status bits.                     */
+       __u32 rate_delivered;   /* saved rate sample: packets delivered */
+       __u32 rate_interval_us; /* saved rate sample: time elapsed */
+       __u32 packets_out;      /* Packets which are "in flight"        */
+       __u32 retrans_out;      /* Retransmitted packets out            */
+       __u32 total_retrans;    /* Total retransmits for entire connection */
+       __u32 segs_in;          /* RFC4898 tcpEStatsPerfSegsIn
+                                * total number of segments in.
                                 */
-       __u32 src_ip6[4];       /* Allows 1,2,4-byte read.
-                                * Stored in network byte order.
+       __u32 data_segs_in;     /* RFC4898 tcpEStatsPerfDataSegsIn
+                                * total number of data segments in.
                                 */
-       __u32 src_port;         /* Allows 4-byte read.
-                                * Stored in host byte order
+       __u32 segs_out;         /* RFC4898 tcpEStatsPerfSegsOut
+                                * The total number of segments sent.
+                                */
+       __u32 data_segs_out;    /* RFC4898 tcpEStatsPerfDataSegsOut
+                                * total number of data segments sent.
+                                */
+       __u32 lost_out;         /* Lost packets                 */
+       __u32 sacked_out;       /* SACK'd packets                       */
+       __u64 bytes_received;   /* RFC4898 tcpEStatsAppHCThruOctetsReceived
+                                * sum(delta(rcv_nxt)), or how many bytes
+                                * were acked.
+                                */
+       __u64 bytes_acked;      /* RFC4898 tcpEStatsAppHCThruOctetsAcked
+                                * sum(delta(snd_una)), or how many bytes
+                                * were acked.
                                 */
 };
 
@@ -3056,4 +3139,7 @@ struct bpf_line_info {
        __u32   line_col;
 };
 
+struct bpf_spin_lock {
+       __u32   val;
+};
 #endif /* _UAPI__LINUX_BPF_H__ */
index 6e52d36..53de880 100644 (file)
@@ -89,6 +89,22 @@ enum devlink_command {
        DEVLINK_CMD_REGION_DEL,
        DEVLINK_CMD_REGION_READ,
 
+       DEVLINK_CMD_PORT_PARAM_GET,     /* can dump */
+       DEVLINK_CMD_PORT_PARAM_SET,
+       DEVLINK_CMD_PORT_PARAM_NEW,
+       DEVLINK_CMD_PORT_PARAM_DEL,
+
+       DEVLINK_CMD_INFO_GET,           /* can dump */
+
+       DEVLINK_CMD_HEALTH_REPORTER_GET,
+       DEVLINK_CMD_HEALTH_REPORTER_SET,
+       DEVLINK_CMD_HEALTH_REPORTER_RECOVER,
+       DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE,
+       DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
+       DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR,
+
+       DEVLINK_CMD_FLASH_UPDATE,
+
        /* add new commands above here */
        __DEVLINK_CMD_MAX,
        DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
@@ -285,6 +301,37 @@ enum devlink_attr {
        DEVLINK_ATTR_REGION_CHUNK_ADDR,         /* u64 */
        DEVLINK_ATTR_REGION_CHUNK_LEN,          /* u64 */
 
+       DEVLINK_ATTR_INFO_DRIVER_NAME,          /* string */
+       DEVLINK_ATTR_INFO_SERIAL_NUMBER,        /* string */
+       DEVLINK_ATTR_INFO_VERSION_FIXED,        /* nested */
+       DEVLINK_ATTR_INFO_VERSION_RUNNING,      /* nested */
+       DEVLINK_ATTR_INFO_VERSION_STORED,       /* nested */
+       DEVLINK_ATTR_INFO_VERSION_NAME,         /* string */
+       DEVLINK_ATTR_INFO_VERSION_VALUE,        /* string */
+
+       DEVLINK_ATTR_SB_POOL_CELL_SIZE,         /* u32 */
+
+       DEVLINK_ATTR_FMSG,                      /* nested */
+       DEVLINK_ATTR_FMSG_OBJ_NEST_START,       /* flag */
+       DEVLINK_ATTR_FMSG_PAIR_NEST_START,      /* flag */
+       DEVLINK_ATTR_FMSG_ARR_NEST_START,       /* flag */
+       DEVLINK_ATTR_FMSG_NEST_END,             /* flag */
+       DEVLINK_ATTR_FMSG_OBJ_NAME,             /* string */
+       DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,       /* u8 */
+       DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA,       /* dynamic */
+
+       DEVLINK_ATTR_HEALTH_REPORTER,                   /* nested */
+       DEVLINK_ATTR_HEALTH_REPORTER_NAME,              /* string */
+       DEVLINK_ATTR_HEALTH_REPORTER_STATE,             /* u8 */
+       DEVLINK_ATTR_HEALTH_REPORTER_ERR,               /* u64 */
+       DEVLINK_ATTR_HEALTH_REPORTER_RECOVER,           /* u64 */
+       DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,           /* u64 */
+       DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,   /* u64 */
+       DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,      /* u8 */
+
+       DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME,    /* string */
+       DEVLINK_ATTR_FLASH_UPDATE_COMPONENT,    /* string */
+
        /* add new attributes above here, update the policy in devlink.c */
 
        __DEVLINK_ATTR_MAX,
index c015120..28491da 100644 (file)
@@ -3,6 +3,7 @@
 #define _UAPI_LINUX_ERRQUEUE_H
 
 #include <linux/types.h>
+#include <linux/time_types.h>
 
 struct sock_extended_err {
        __u32   ee_errno;       
@@ -41,6 +42,10 @@ struct scm_timestamping {
        struct timespec ts[3];
 };
 
+struct scm_timestamping64 {
+       struct __kernel_timespec ts[3];
+};
+
 /* The type of scm_timestamping, passed in sock_extended_err ee_info.
  * This defines the type of ts[0]. For SCM_TSTAMP_SND only, if ts[0]
  * is zero, then this is a hardware timestamp and recorded in ts[2].
index 14565d7..e8baca8 100644 (file)
@@ -137,15 +137,21 @@ enum {
        INET_DIAG_TCLASS,
        INET_DIAG_SKMEMINFO,
        INET_DIAG_SHUTDOWN,
-       INET_DIAG_DCTCPINFO,
-       INET_DIAG_PROTOCOL,  /* response attribute only */
+
+       /*
+        * Next extenstions cannot be requested in struct inet_diag_req_v2:
+        * its field idiag_ext has only 8 bits.
+        */
+
+       INET_DIAG_DCTCPINFO,    /* request as INET_DIAG_VEGASINFO */
+       INET_DIAG_PROTOCOL,     /* response attribute only */
        INET_DIAG_SKV6ONLY,
        INET_DIAG_LOCALS,
        INET_DIAG_PEERS,
        INET_DIAG_PAD,
-       INET_DIAG_MARK,
-       INET_DIAG_BBRINFO,
-       INET_DIAG_CLASS_ID,
+       INET_DIAG_MARK,         /* only with CAP_NET_ADMIN */
+       INET_DIAG_BBRINFO,      /* request as INET_DIAG_VEGASINFO */
+       INET_DIAG_CLASS_ID,     /* request as INET_DIAG_TCLASS */
        INET_DIAG_MD5SIG,
        __INET_DIAG_MAX,
 };
index d435b00..0a55206 100644 (file)
@@ -45,6 +45,7 @@
 #define MDIO_AN_ADVERTISE      16      /* AN advertising (base page) */
 #define MDIO_AN_LPA            19      /* AN LP abilities (base page) */
 #define MDIO_PCS_EEE_ABLE      20      /* EEE Capability register */
+#define MDIO_PMA_NG_EXTABLE    21      /* 2.5G/5G PMA/PMD extended ability */
 #define MDIO_PCS_EEE_WK_ERR    22      /* EEE wake error counter */
 #define MDIO_PHYXS_LNSTAT      24      /* PHY XGXS lane state */
 #define MDIO_AN_EEE_ADV                60      /* EEE advertisement */
 #define MDIO_CTRL1_SPEED10G            (MDIO_CTRL1_SPEEDSELEXT | 0x00)
 /* 10PASS-TS/2BASE-TL */
 #define MDIO_CTRL1_SPEED10P2B          (MDIO_CTRL1_SPEEDSELEXT | 0x04)
+/* 2.5 Gb/s */
+#define MDIO_CTRL1_SPEED2_5G           (MDIO_CTRL1_SPEEDSELEXT | 0x18)
+/* 5 Gb/s */
+#define MDIO_CTRL1_SPEED5G             (MDIO_CTRL1_SPEEDSELEXT | 0x1c)
 
 /* Status register 1. */
 #define MDIO_STAT1_LPOWERABLE          0x0002  /* Low-power ability */
 
 /* Device present registers. */
 #define MDIO_DEVS_PRESENT(devad)       (1 << (devad))
+#define MDIO_DEVS_C22PRESENT           MDIO_DEVS_PRESENT(0)
 #define MDIO_DEVS_PMAPMD               MDIO_DEVS_PRESENT(MDIO_MMD_PMAPMD)
 #define MDIO_DEVS_WIS                  MDIO_DEVS_PRESENT(MDIO_MMD_WIS)
 #define MDIO_DEVS_PCS                  MDIO_DEVS_PRESENT(MDIO_MMD_PCS)
 #define MDIO_DEVS_TC                   MDIO_DEVS_PRESENT(MDIO_MMD_TC)
 #define MDIO_DEVS_AN                   MDIO_DEVS_PRESENT(MDIO_MMD_AN)
 #define MDIO_DEVS_C22EXT               MDIO_DEVS_PRESENT(MDIO_MMD_C22EXT)
+#define MDIO_DEVS_VEND1                        MDIO_DEVS_PRESENT(MDIO_MMD_VEND1)
+#define MDIO_DEVS_VEND2                        MDIO_DEVS_PRESENT(MDIO_MMD_VEND2)
 
 /* Control register 2. */
 #define MDIO_PMA_CTRL2_TYPE            0x000f  /* PMA/PMD type selection */
 #define MDIO_PMA_CTRL2_1000BKX         0x000d  /* 1000BASE-KX type */
 #define MDIO_PMA_CTRL2_100BTX          0x000e  /* 100BASE-TX type */
 #define MDIO_PMA_CTRL2_10BT            0x000f  /* 10BASE-T type */
+#define MDIO_PMA_CTRL2_2_5GBT          0x0030  /* 2.5GBaseT type */
+#define MDIO_PMA_CTRL2_5GBT            0x0031  /* 5GBaseT type */
 #define MDIO_PCS_CTRL2_TYPE            0x0003  /* PCS type selection */
 #define MDIO_PCS_CTRL2_10GBR           0x0000  /* 10GBASE-R type */
 #define MDIO_PCS_CTRL2_10GBX           0x0001  /* 10GBASE-X type */
 #define MDIO_PMA_EXTABLE_1000BKX       0x0040  /* 1000BASE-KX ability */
 #define MDIO_PMA_EXTABLE_100BTX                0x0080  /* 100BASE-TX ability */
 #define MDIO_PMA_EXTABLE_10BT          0x0100  /* 10BASE-T ability */
+#define MDIO_PMA_EXTABLE_NBT           0x4000  /* 2.5/5GBASE-T ability */
 
 /* PHY XGXS lane state register. */
 #define MDIO_PHYXS_LNSTAT_SYNC0                0x0001
 #define MDIO_PCS_10GBRT_STAT2_BER      0x3f00
 
 /* AN 10GBASE-T control register. */
+#define MDIO_AN_10GBT_CTRL_ADV2_5G     0x0080  /* Advertise 2.5GBASE-T */
+#define MDIO_AN_10GBT_CTRL_ADV5G       0x0100  /* Advertise 5GBASE-T */
 #define MDIO_AN_10GBT_CTRL_ADV10G      0x1000  /* Advertise 10GBASE-T */
 
 /* AN 10GBASE-T status register. */
+#define MDIO_AN_10GBT_STAT_LP2_5G      0x0020  /* LP is 2.5GBT capable */
+#define MDIO_AN_10GBT_STAT_LP5G                0x0040  /* LP is 5GBT capable */
 #define MDIO_AN_10GBT_STAT_LPTRR       0x0200  /* LP training reset req. */
 #define MDIO_AN_10GBT_STAT_LPLTABLE    0x0400  /* LP loop timing ability */
 #define MDIO_AN_10GBT_STAT_LP10G       0x0800  /* LP is 10GBT capable */
 #define MDIO_EEE_10GKX4                0x0020  /* 10G KX4 EEE cap */
 #define MDIO_EEE_10GKR         0x0040  /* 10G KR EEE cap */
 
+/* 2.5G/5G Extended abilities register. */
+#define MDIO_PMA_NG_EXTABLE_2_5GBT     0x0001  /* 2.5GBASET ability */
+#define MDIO_PMA_NG_EXTABLE_5GBT       0x0002  /* 5GBASET ability */
+
 /* LASI RX_ALARM control/status registers. */
 #define MDIO_PMA_LASI_RX_PHYXSLFLT     0x0001  /* PHY XS RX local fault */
 #define MDIO_PMA_LASI_RX_PCSLFLT       0x0008  /* PCS RX local fault */
index 31ae5c7..dd4f86e 100644 (file)
@@ -1565,6 +1565,12 @@ enum nl80211_commands {
  *     (a u32 with flags from &enum nl80211_wpa_versions).
  * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
  *     indicate which key management algorithm(s) to use (an array of u32).
+ *     This attribute is also sent in response to @NL80211_CMD_GET_WIPHY,
+ *     indicating the supported AKM suites, intended for specific drivers which
+ *     implement SME and have constraints on which AKMs are supported and also
+ *     the cases where an AKM support is offloaded to the driver/firmware.
+ *     If there is no such notification from the driver, user space should
+ *     assume the driver supports all the AKM suites.
  *
  * @NL80211_ATTR_REQ_IE: (Re)association request information elements as
  *     sent out by the card, for ROAM and successful CONNECT events.
@@ -2260,10 +2266,10 @@ enum nl80211_commands {
  *     &enum nl80211_external_auth_action value). This is used with the
  *     %NL80211_CMD_EXTERNAL_AUTH request event.
  * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user
- *     space supports external authentication. This attribute shall be used
- *     only with %NL80211_CMD_CONNECT request. The driver may offload
- *     authentication processing to user space if this capability is indicated
- *     in NL80211_CMD_CONNECT requests from the user space.
+ *     space supports external authentication. This attribute shall be used
+ *     with %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP request. The driver
+ *     may offload authentication processing to user space if this capability
+ *     is indicated in the respective requests from the user space.
  *
  * @NL80211_ATTR_NSS: Station's New/updated  RX_NSS value notified using this
  *     u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
@@ -2299,6 +2305,9 @@ enum nl80211_commands {
  *     This is also used for capability advertisement in the wiphy information,
  *     with the appropriate sub-attributes.
  *
+ * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime
+ *     scheduler.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2748,6 +2757,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_PEER_MEASUREMENTS,
 
+       NL80211_ATTR_AIRTIME_WEIGHT,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3125,6 +3136,9 @@ enum nl80211_sta_bss_param {
  *     might not be fully accurate.
  * @NL80211_STA_INFO_CONNECTED_TO_GATE: set to true if STA has a path to a
  *     mesh gate (u8, 0 or 1)
+ * @NL80211_STA_INFO_TX_DURATION: aggregate PPDU duration for all frames
+ *     sent to the station (u64, usec)
+ * @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16)
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -3168,6 +3182,8 @@ enum nl80211_sta_info {
        NL80211_STA_INFO_RX_MPDUS,
        NL80211_STA_INFO_FCS_ERROR_COUNT,
        NL80211_STA_INFO_CONNECTED_TO_GATE,
+       NL80211_STA_INFO_TX_DURATION,
+       NL80211_STA_INFO_AIRTIME_WEIGHT,
 
        /* keep last */
        __NL80211_STA_INFO_AFTER_LAST,
@@ -3277,8 +3293,10 @@ enum nl80211_mpath_flags {
  *     &enum nl80211_mpath_flags;
  * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
  * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+ * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination
+ * @NL80211_MPATH_INFO_PATH_CHANGE: total number of path changes to destination
  * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number
- *     currently defind
+ *     currently defined
  * @__NL80211_MPATH_INFO_AFTER_LAST: internal use
  */
 enum nl80211_mpath_info {
@@ -3290,6 +3308,8 @@ enum nl80211_mpath_info {
        NL80211_MPATH_INFO_FLAGS,
        NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
        NL80211_MPATH_INFO_DISCOVERY_RETRIES,
+       NL80211_MPATH_INFO_HOP_COUNT,
+       NL80211_MPATH_INFO_PATH_CHANGE,
 
        /* keep last */
        __NL80211_MPATH_INFO_AFTER_LAST,
@@ -5316,6 +5336,13 @@ enum nl80211_feature_flags {
  *      if this flag is not set. Ignoring this can leak clear text packets and/or
  *      freeze the connection.
  *
+ * @NL80211_EXT_FEATURE_AIRTIME_FAIRNESS: Driver supports getting airtime
+ *     fairness for transmitted packets and has enabled airtime fairness
+ *     scheduling.
+ *
+ * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
+ *     (set/del PMKSA operations) in AP mode.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -5355,6 +5382,8 @@ enum nl80211_ext_feature_index {
        NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT,
        NL80211_EXT_FEATURE_CAN_REPLACE_PTK0,
        NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
+       NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
+       NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
 
        /* add new features before the definition below */
        NUM_NL80211_EXT_FEATURES,
@@ -5606,9 +5635,14 @@ enum nl80211_crit_proto_id {
  * Used by cfg80211_rx_mgmt()
  *
  * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver.
+ * @NL80211_RXMGMT_FLAG_EXTERNAL_AUTH: Host driver intends to offload
+ *     the authentication. Exclusively defined for host drivers that
+ *     advertises the SME functionality but would like the userspace
+ *     to handle certain authentication algorithms (e.g. SAE).
  */
 enum nl80211_rxmgmt_flags {
        NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0,
+       NL80211_RXMGMT_FLAG_EXTERNAL_AUTH = 1 << 1,
 };
 
 /*
index 02ac251..51a0496 100644 (file)
@@ -63,12 +63,49 @@ enum {
 #define TC_ACT_GOTO_CHAIN __TC_ACT_EXT(2)
 #define TC_ACT_EXT_OPCODE_MAX  TC_ACT_GOTO_CHAIN
 
+/* These macros are put here for binary compatibility with userspace apps that
+ * make use of them. For kernel code and new userspace apps, use the TCA_ID_*
+ * versions.
+ */
+#define TCA_ACT_GACT 5
+#define TCA_ACT_IPT 6
+#define TCA_ACT_PEDIT 7
+#define TCA_ACT_MIRRED 8
+#define TCA_ACT_NAT 9
+#define TCA_ACT_XT 10
+#define TCA_ACT_SKBEDIT 11
+#define TCA_ACT_VLAN 12
+#define TCA_ACT_BPF 13
+#define TCA_ACT_CONNMARK 14
+#define TCA_ACT_SKBMOD 15
+#define TCA_ACT_CSUM 16
+#define TCA_ACT_TUNNEL_KEY 17
+#define TCA_ACT_SIMP 22
+#define TCA_ACT_IFE 25
+#define TCA_ACT_SAMPLE 26
+
 /* Action type identifiers*/
-enum {
-       TCA_ID_UNSPEC=0,
-       TCA_ID_POLICE=1,
+enum tca_id {
+       TCA_ID_UNSPEC = 0,
+       TCA_ID_POLICE = 1,
+       TCA_ID_GACT = TCA_ACT_GACT,
+       TCA_ID_IPT = TCA_ACT_IPT,
+       TCA_ID_PEDIT = TCA_ACT_PEDIT,
+       TCA_ID_MIRRED = TCA_ACT_MIRRED,
+       TCA_ID_NAT = TCA_ACT_NAT,
+       TCA_ID_XT = TCA_ACT_XT,
+       TCA_ID_SKBEDIT = TCA_ACT_SKBEDIT,
+       TCA_ID_VLAN = TCA_ACT_VLAN,
+       TCA_ID_BPF = TCA_ACT_BPF,
+       TCA_ID_CONNMARK = TCA_ACT_CONNMARK,
+       TCA_ID_SKBMOD = TCA_ACT_SKBMOD,
+       TCA_ID_CSUM = TCA_ACT_CSUM,
+       TCA_ID_TUNNEL_KEY = TCA_ACT_TUNNEL_KEY,
+       TCA_ID_SIMP = TCA_ACT_SIMP,
+       TCA_ID_IFE = TCA_ACT_IFE,
+       TCA_ID_SAMPLE = TCA_ACT_SAMPLE,
        /* other actions go here */
-       __TCA_ID_MAX=255
+       __TCA_ID_MAX = 255
 };
 
 #define TCA_ID_MAX __TCA_ID_MAX
index 8b73cb6..5d0f76c 100644 (file)
 #define RDS_TRANS_COUNT        3
 #define        RDS_TRANS_NONE  (~0)
 
+/* IOCTLS commands for SOL_RDS */
+#define SIOCRDSSETTOS          (SIOCPROTOPRIVATE)
+#define SIOCRDSGETTOS          (SIOCPROTOPRIVATE + 1)
+
+typedef __u8   rds_tos_t;
+
 /*
  * Control message types for SOL_RDS.
  *
@@ -149,6 +155,7 @@ struct rds_info_connection {
        __be32          faddr;
        __u8            transport[TRANSNAMSIZ];         /* null term ascii */
        __u8            flags;
+       __u8            tos;
 } __attribute__((packed));
 
 struct rds6_info_connection {
@@ -171,6 +178,7 @@ struct rds_info_message {
        __be16          lport;
        __be16          fport;
        __u8            flags;
+       __u8            tos;
 } __attribute__((packed));
 
 struct rds6_info_message {
@@ -214,6 +222,7 @@ struct rds_info_tcp_socket {
        __u32           last_sent_nxt;
        __u32           last_expected_una;
        __u32           last_seen_una;
+       __u8            tos;
 } __attribute__((packed));
 
 struct rds6_info_tcp_socket {
@@ -240,6 +249,7 @@ struct rds_info_rdma_connection {
        __u32           max_send_sge;
        __u32           rdma_mr_max;
        __u32           rdma_mr_size;
+       __u8            tos;
 };
 
 struct rds6_info_rdma_connection {
@@ -253,6 +263,7 @@ struct rds6_info_rdma_connection {
        __u32           max_send_sge;
        __u32           rdma_mr_max;
        __u32           rdma_mr_size;
+       __u8            tos;
 };
 
 /* RDS message Receive Path Latency points */
index d584073..b8f2c4d 100644 (file)
 
 typedef __s32 sctp_assoc_t;
 
+#define SCTP_FUTURE_ASSOC      0
+#define SCTP_CURRENT_ASSOC     1
+#define SCTP_ALL_ASSOC         2
+
 /* The following symbols come from the Sockets API Extensions for
  * SCTP <draft-ietf-tsvwg-sctpsocket-07.txt>.
  */
index 6e89a5d..653c4f9 100644 (file)
@@ -13,8 +13,6 @@
 
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_BPF 13
-
 struct tc_act_bpf {
        tc_gen;
 };
index 80caa47..9f8f6f7 100644 (file)
@@ -5,8 +5,6 @@
 #include <linux/types.h>
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_CONNMARK 14
-
 struct tc_connmark {
        tc_gen;
        __u16 zone;
index 0ecf4d2..94b2044 100644 (file)
@@ -5,8 +5,6 @@
 #include <linux/types.h>
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_CSUM 16
-
 enum {
        TCA_CSUM_UNSPEC,
        TCA_CSUM_PARMS,
index 94273c3..37e5392 100644 (file)
@@ -5,7 +5,6 @@
 #include <linux/types.h>
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_GACT 5
 struct tc_gact {
        tc_gen;
 
index 2f48490..8c401f1 100644 (file)
@@ -6,7 +6,6 @@
 #include <linux/pkt_cls.h>
 #include <linux/ife.h>
 
-#define TCA_ACT_IFE 25
 /* Flag bits for now just encoding/decoding; mutually exclusive */
 #define IFE_ENCODE 1
 #define IFE_DECODE 0
index b743c8b..c48d7da 100644 (file)
@@ -4,9 +4,6 @@
 
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_IPT 6
-#define TCA_ACT_XT 10
-
 enum {
        TCA_IPT_UNSPEC,
        TCA_IPT_TABLE,
index 5dd671c..2500a00 100644 (file)
@@ -5,7 +5,6 @@
 #include <linux/types.h>
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_MIRRED 8
 #define TCA_EGRESS_REDIR 1  /* packet redirect to EGRESS*/
 #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
 #define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
index 086be84..21399c2 100644 (file)
@@ -5,8 +5,6 @@
 #include <linux/pkt_cls.h>
 #include <linux/types.h>
 
-#define TCA_ACT_NAT 9
-
 enum {
        TCA_NAT_UNSPEC,
        TCA_NAT_PARMS,
index 24ec792..f3e61b0 100644 (file)
@@ -5,8 +5,6 @@
 #include <linux/types.h>
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_PEDIT 7
-
 enum {
        TCA_PEDIT_UNSPEC,
        TCA_PEDIT_TM,
index bd7e9f0..fee1bcc 100644 (file)
@@ -6,8 +6,6 @@
 #include <linux/pkt_cls.h>
 #include <linux/if_ether.h>
 
-#define TCA_ACT_SAMPLE 26
-
 struct tc_sample {
        tc_gen;
 };
index 6de6071..800e933 100644 (file)
@@ -23,8 +23,6 @@
 
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_SKBEDIT 11
-
 #define SKBEDIT_F_PRIORITY             0x1
 #define SKBEDIT_F_QUEUE_MAPPING                0x2
 #define SKBEDIT_F_MARK                 0x4
index 38c072f..c525b35 100644 (file)
@@ -13,8 +13,6 @@
 
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_SKBMOD 15
-
 #define SKBMOD_F_DMAC  0x1
 #define SKBMOD_F_SMAC  0x2
 #define SKBMOD_F_ETYPE 0x4
index be384d6..41c8b46 100644 (file)
@@ -14,8 +14,6 @@
 
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_TUNNEL_KEY 17
-
 #define TCA_TUNNEL_KEY_ACT_SET     1
 #define TCA_TUNNEL_KEY_ACT_RELEASE  2
 
index 0d7b5fd..168995b 100644 (file)
@@ -13,8 +13,6 @@
 
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_VLAN 12
-
 #define TCA_VLAN_ACT_POP       1
 #define TCA_VLAN_ACT_PUSH      2
 #define TCA_VLAN_ACT_MODIFY    3
index 6b56a22..958932e 100644 (file)
@@ -3,7 +3,7 @@
 #define _UAPI_LINUX_TIME_H
 
 #include <linux/types.h>
-
+#include <linux/time_types.h>
 
 #ifndef _STRUCT_TIMESPEC
 #define _STRUCT_TIMESPEC
@@ -23,7 +23,6 @@ struct timezone {
        int     tz_dsttime;     /* type of dst correction */
 };
 
-
 /*
  * Names of the interval timers, and structure
  * defining a timer setting:
@@ -42,32 +41,6 @@ struct itimerval {
        struct timeval it_value;        /* current value */
 };
 
-#ifndef __kernel_timespec
-struct __kernel_timespec {
-       __kernel_time64_t       tv_sec;                 /* seconds */
-       long long               tv_nsec;                /* nanoseconds */
-};
-#endif
-
-#ifndef __kernel_itimerspec
-struct __kernel_itimerspec {
-       struct __kernel_timespec it_interval;    /* timer period */
-       struct __kernel_timespec it_value;       /* timer expiration */
-};
-#endif
-
-/*
- * legacy timeval structure, only embedded in structures that
- * traditionally used 'timeval' to pass time intervals (not absolute
- * times). Do not add new users. If user space fails to compile
- * here, this is probably because it is not y2038 safe and needs to
- * be changed to use another interface.
- */
-struct __kernel_old_timeval {
-       __kernel_long_t tv_sec;
-       __kernel_long_t tv_usec;
-};
-
 /*
  * The IDs of the various system clocks (for POSIX.1b interval timers):
  */
diff --git a/include/uapi/linux/time_types.h b/include/uapi/linux/time_types.h
new file mode 100644 (file)
index 0000000..459070c
--- /dev/null
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_TIME_TYPES_H
+#define _UAPI_LINUX_TIME_TYPES_H
+
+#include <linux/types.h>
+
+#ifndef __kernel_timespec
+struct __kernel_timespec {
+       __kernel_time64_t       tv_sec;                 /* seconds */
+       long long               tv_nsec;                /* nanoseconds */
+};
+#endif
+
+#ifndef __kernel_itimerspec
+struct __kernel_itimerspec {
+       struct __kernel_timespec it_interval;    /* timer period */
+       struct __kernel_timespec it_value;       /* timer expiration */
+};
+#endif
+
+/*
+ * legacy timeval structure, only embedded in structures that
+ * traditionally used 'timeval' to pass time intervals (not absolute
+ * times). Do not add new users. If user space fails to compile
+ * here, this is probably because it is not y2038 safe and needs to
+ * be changed to use another interface.
+ */
+#ifndef __kernel_old_timeval
+struct __kernel_old_timeval {
+       __kernel_long_t tv_sec;
+       __kernel_long_t tv_usec;
+};
+#endif
+
+struct __kernel_sock_timeval {
+       __s64 tv_sec;
+       __s64 tv_usec;
+};
+
+#endif /* _UAPI_LINUX_TIME_TYPES_H */
index ff02287..401d6f0 100644 (file)
 #define TLS_1_2_VERSION_MINOR  0x3
 #define TLS_1_2_VERSION                TLS_VERSION_NUMBER(TLS_1_2)
 
+#define TLS_1_3_VERSION_MAJOR  0x3
+#define TLS_1_3_VERSION_MINOR  0x4
+#define TLS_1_3_VERSION                TLS_VERSION_NUMBER(TLS_1_3)
+
 /* Supported ciphers */
 #define TLS_CIPHER_AES_GCM_128                         51
 #define TLS_CIPHER_AES_GCM_128_IV_SIZE                 8
 #define TLS_CIPHER_AES_GCM_128_TAG_SIZE                16
 #define TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE            8
 
+#define TLS_CIPHER_AES_GCM_256                         52
+#define TLS_CIPHER_AES_GCM_256_IV_SIZE                 8
+#define TLS_CIPHER_AES_GCM_256_KEY_SIZE                32
+#define TLS_CIPHER_AES_GCM_256_SALT_SIZE               4
+#define TLS_CIPHER_AES_GCM_256_TAG_SIZE                16
+#define TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE            8
+
 #define TLS_SET_RECORD_TYPE    1
 #define TLS_GET_RECORD_TYPE    2
 
@@ -75,4 +86,12 @@ struct tls12_crypto_info_aes_gcm_128 {
        unsigned char rec_seq[TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE];
 };
 
+struct tls12_crypto_info_aes_gcm_256 {
+       struct tls_crypto_info info;
+       unsigned char iv[TLS_CIPHER_AES_GCM_256_IV_SIZE];
+       unsigned char key[TLS_CIPHER_AES_GCM_256_KEY_SIZE];
+       unsigned char salt[TLS_CIPHER_AES_GCM_256_SALT_SIZE];
+       unsigned char rec_seq[TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE];
+};
+
 #endif /* _UAPI_LINUX_TLS_H */
index 1196e1c..ff8e7dc 100644 (file)
 #define VIRTIO_F_RING_PACKED           34
 
 /*
+ * This feature indicates that memory accesses by the driver and the
+ * device are ordered in a way described by the platform.
+ */
+#define VIRTIO_F_ORDER_PLATFORM                36
+
+/*
  * Does the device support Single Root I/O Virtualization?
  */
 #define VIRTIO_F_SR_IOV                        37
index 2414f8a..4c4e24c 100644 (file)
@@ -213,14 +213,4 @@ struct vring_packed_desc {
        __le16 flags;
 };
 
-struct vring_packed {
-       unsigned int num;
-
-       struct vring_packed_desc *desc;
-
-       struct vring_packed_desc_event *driver;
-
-       struct vring_packed_desc_event *device;
-};
-
 #endif /* _UAPI_LINUX_VIRTIO_RING_H */
index ef3c7ec..eb76b38 100644 (file)
@@ -52,6 +52,11 @@ struct hns_roce_ib_create_srq {
        __aligned_u64 que_addr;
 };
 
+struct hns_roce_ib_create_srq_resp {
+       __u32   srqn;
+       __u32   reserved;
+};
+
 struct hns_roce_ib_create_qp {
        __aligned_u64 buf_addr;
        __aligned_u64 db_addr;
index 513fa54..c9386a3 100644 (file)
@@ -512,6 +512,17 @@ config PSI_DEFAULT_DISABLED
          per default but can be enabled through passing psi=1 on the
          kernel commandline during boot.
 
+         This feature adds some code to the task wakeup and sleep
+         paths of the scheduler. The overhead is too low to affect
+         common scheduling-intense workloads in practice (such as
+         webservers, memcache), but it does show up in artificial
+         scheduler stress tests, such as hackbench.
+
+         If you are paranoid and not sure what the kernel will be
+         used for, say Y.
+
+         Say N if unsure.
+
 endmenu # "CPU/Task time and stats accounting"
 
 config CPU_ISOLATION
@@ -825,7 +836,7 @@ config CGROUP_PIDS
          PIDs controller is designed to stop this from happening.
 
          It should be noted that organisational operations (such as attaching
-         to a cgroup hierarchy will *not* be blocked by the PIDs controller),
+         to a cgroup hierarchy) will *not* be blocked by the PIDs controller,
          since the PIDs limit only affects a process's ability to fork, not to
          attach to a cgroup.
 
index e2e80ca..c86a1c8 100644 (file)
@@ -695,7 +695,6 @@ asmlinkage __visible void __init start_kernel(void)
                initrd_start = 0;
        }
 #endif
-       page_ext_init();
        kmemleak_init();
        setup_per_cpu_pageset();
        numa_policy_init();
@@ -1131,6 +1130,8 @@ static noinline void __init kernel_init_freeable(void)
        sched_init_smp();
 
        page_alloc_init_late();
+       /* Initialize page ext after all struct pages are initialized. */
+       page_ext_init();
 
        do_basic_setup();
 
index 84d882f..fbba478 100644 (file)
@@ -242,6 +242,9 @@ config QUEUED_SPINLOCKS
        def_bool y if ARCH_USE_QUEUED_SPINLOCKS
        depends on SMP
 
+config BPF_ARCH_SPINLOCK
+       bool
+
 config ARCH_USE_QUEUED_RWLOCKS
        bool
 
index 25632a7..c72e0d8 100644 (file)
@@ -253,8 +253,9 @@ static int array_map_update_elem(struct bpf_map *map, void *key, void *value,
 {
        struct bpf_array *array = container_of(map, struct bpf_array, map);
        u32 index = *(u32 *)key;
+       char *val;
 
-       if (unlikely(map_flags > BPF_EXIST))
+       if (unlikely((map_flags & ~BPF_F_LOCK) > BPF_EXIST))
                /* unknown flags */
                return -EINVAL;
 
@@ -262,17 +263,25 @@ static int array_map_update_elem(struct bpf_map *map, void *key, void *value,
                /* all elements were pre-allocated, cannot insert a new one */
                return -E2BIG;
 
-       if (unlikely(map_flags == BPF_NOEXIST))
+       if (unlikely(map_flags & BPF_NOEXIST))
                /* all elements already exist */
                return -EEXIST;
 
-       if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY)
+       if (unlikely((map_flags & BPF_F_LOCK) &&
+                    !map_value_has_spin_lock(map)))
+               return -EINVAL;
+
+       if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) {
                memcpy(this_cpu_ptr(array->pptrs[index & array->index_mask]),
                       value, map->value_size);
-       else
-               memcpy(array->value +
-                      array->elem_size * (index & array->index_mask),
-                      value, map->value_size);
+       } else {
+               val = array->value +
+                       array->elem_size * (index & array->index_mask);
+               if (map_flags & BPF_F_LOCK)
+                       copy_map_value_locked(map, val, value, false);
+               else
+                       copy_map_value(map, val, value);
+       }
        return 0;
 }
 
index 3d661f0..bd3921b 100644 (file)
@@ -355,6 +355,11 @@ static bool btf_type_is_struct(const struct btf_type *t)
        return kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION;
 }
 
+static bool __btf_type_is_struct(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_STRUCT;
+}
+
 static bool btf_type_is_array(const struct btf_type *t)
 {
        return BTF_INFO_KIND(t->info) == BTF_KIND_ARRAY;
@@ -1525,7 +1530,8 @@ static int btf_modifier_resolve(struct btf_verifier_env *env,
 
                /* "typedef void new_void", "const void"...etc */
                if (!btf_type_is_void(next_type) &&
-                   !btf_type_is_fwd(next_type)) {
+                   !btf_type_is_fwd(next_type) &&
+                   !btf_type_is_func_proto(next_type)) {
                        btf_verifier_log_type(env, v->t, "Invalid type_id");
                        return -EINVAL;
                }
@@ -2045,6 +2051,43 @@ static void btf_struct_log(struct btf_verifier_env *env,
        btf_verifier_log(env, "size=%u vlen=%u", t->size, btf_type_vlen(t));
 }
 
+/* find 'struct bpf_spin_lock' in map value.
+ * return >= 0 offset if found
+ * and < 0 in case of error
+ */
+int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t)
+{
+       const struct btf_member *member;
+       u32 i, off = -ENOENT;
+
+       if (!__btf_type_is_struct(t))
+               return -EINVAL;
+
+       for_each_member(i, t, member) {
+               const struct btf_type *member_type = btf_type_by_id(btf,
+                                                                   member->type);
+               if (!__btf_type_is_struct(member_type))
+                       continue;
+               if (member_type->size != sizeof(struct bpf_spin_lock))
+                       continue;
+               if (strcmp(__btf_name_by_offset(btf, member_type->name_off),
+                          "bpf_spin_lock"))
+                       continue;
+               if (off != -ENOENT)
+                       /* only one 'struct bpf_spin_lock' is allowed */
+                       return -E2BIG;
+               off = btf_member_bit_offset(t, member);
+               if (off % 8)
+                       /* valid C code cannot generate such BTF */
+                       return -EINVAL;
+               off /= 8;
+               if (off % __alignof__(struct bpf_spin_lock))
+                       /* valid struct bpf_spin_lock will be 4 byte aligned */
+                       return -EINVAL;
+       }
+       return off;
+}
+
 static void btf_struct_seq_show(const struct btf *btf, const struct btf_type *t,
                                u32 type_id, void *data, u8 bits_offset,
                                struct seq_file *m)
index ab612fe..4e80797 100644 (file)
@@ -230,6 +230,7 @@ cleanup:
  * @cgrp: The cgroup which descendants to traverse
  * @prog: A program to attach
  * @type: Type of attach operation
+ * @flags: Option flags
  *
  * Must be called with cgroup_mutex held.
  */
@@ -363,7 +364,7 @@ cleanup:
  * Must be called with cgroup_mutex held.
  */
 int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
-                       enum bpf_attach_type type, u32 unused_flags)
+                       enum bpf_attach_type type)
 {
        struct list_head *progs = &cgrp->bpf.progs[type];
        enum bpf_cgroup_storage_type stype;
@@ -572,7 +573,7 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk,
        bpf_compute_and_save_data_end(skb, &saved_data_end);
 
        ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], skb,
-                                bpf_prog_run_save_cb);
+                                __bpf_prog_run_save_cb);
        bpf_restore_data_end(skb, saved_data_end);
        __skb_pull(skb, offset);
        skb->sk = save_sk;
index a7bcb23..ef88b16 100644 (file)
@@ -1263,8 +1263,9 @@ bool bpf_opcode_in_insntable(u8 code)
 #ifndef CONFIG_BPF_JIT_ALWAYS_ON
 /**
  *     __bpf_prog_run - run eBPF program on a given context
- *     @ctx: is the data we are operating on
+ *     @regs: is the array of MAX_BPF_EXT_REG eBPF pseudo-registers
  *     @insn: is the array of eBPF instructions
+ *     @stack: is the eBPF storage stack
  *
  * Decode and execute eBPF instructions.
  */
@@ -2001,6 +2002,8 @@ const struct bpf_func_proto bpf_map_delete_elem_proto __weak;
 const struct bpf_func_proto bpf_map_push_elem_proto __weak;
 const struct bpf_func_proto bpf_map_pop_elem_proto __weak;
 const struct bpf_func_proto bpf_map_peek_elem_proto __weak;
+const struct bpf_func_proto bpf_spin_lock_proto __weak;
+const struct bpf_func_proto bpf_spin_unlock_proto __weak;
 
 const struct bpf_func_proto bpf_get_prandom_u32_proto __weak;
 const struct bpf_func_proto bpf_get_smp_processor_id_proto __weak;
index 4b7c767..fed15cf 100644 (file)
@@ -686,7 +686,7 @@ static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l)
        }
 
        if (htab_is_prealloc(htab)) {
-               pcpu_freelist_push(&htab->freelist, &l->fnode);
+               __pcpu_freelist_push(&htab->freelist, &l->fnode);
        } else {
                atomic_dec(&htab->count);
                l->htab = htab;
@@ -718,21 +718,12 @@ static bool fd_htab_map_needs_adjust(const struct bpf_htab *htab)
               BITS_PER_LONG == 64;
 }
 
-static u32 htab_size_value(const struct bpf_htab *htab, bool percpu)
-{
-       u32 size = htab->map.value_size;
-
-       if (percpu || fd_htab_map_needs_adjust(htab))
-               size = round_up(size, 8);
-       return size;
-}
-
 static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
                                         void *value, u32 key_size, u32 hash,
                                         bool percpu, bool onallcpus,
                                         struct htab_elem *old_elem)
 {
-       u32 size = htab_size_value(htab, percpu);
+       u32 size = htab->map.value_size;
        bool prealloc = htab_is_prealloc(htab);
        struct htab_elem *l_new, **pl_new;
        void __percpu *pptr;
@@ -748,7 +739,7 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
                } else {
                        struct pcpu_freelist_node *l;
 
-                       l = pcpu_freelist_pop(&htab->freelist);
+                       l = __pcpu_freelist_pop(&htab->freelist);
                        if (!l)
                                return ERR_PTR(-E2BIG);
                        l_new = container_of(l, struct htab_elem, fnode);
@@ -770,10 +761,13 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
                        l_new = ERR_PTR(-ENOMEM);
                        goto dec_count;
                }
+               check_and_init_map_lock(&htab->map,
+                                       l_new->key + round_up(key_size, 8));
        }
 
        memcpy(l_new->key, key, key_size);
        if (percpu) {
+               size = round_up(size, 8);
                if (prealloc) {
                        pptr = htab_elem_get_ptr(l_new, key_size);
                } else {
@@ -791,8 +785,13 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
 
                if (!prealloc)
                        htab_elem_set_ptr(l_new, key_size, pptr);
-       } else {
+       } else if (fd_htab_map_needs_adjust(htab)) {
+               size = round_up(size, 8);
                memcpy(l_new->key + round_up(key_size, 8), value, size);
+       } else {
+               copy_map_value(&htab->map,
+                              l_new->key + round_up(key_size, 8),
+                              value);
        }
 
        l_new->hash = hash;
@@ -805,11 +804,11 @@ dec_count:
 static int check_flags(struct bpf_htab *htab, struct htab_elem *l_old,
                       u64 map_flags)
 {
-       if (l_old && map_flags == BPF_NOEXIST)
+       if (l_old && (map_flags & ~BPF_F_LOCK) == BPF_NOEXIST)
                /* elem already exists */
                return -EEXIST;
 
-       if (!l_old && map_flags == BPF_EXIST)
+       if (!l_old && (map_flags & ~BPF_F_LOCK) == BPF_EXIST)
                /* elem doesn't exist, cannot update it */
                return -ENOENT;
 
@@ -828,7 +827,7 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
        u32 key_size, hash;
        int ret;
 
-       if (unlikely(map_flags > BPF_EXIST))
+       if (unlikely((map_flags & ~BPF_F_LOCK) > BPF_EXIST))
                /* unknown flags */
                return -EINVAL;
 
@@ -841,6 +840,28 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
        b = __select_bucket(htab, hash);
        head = &b->head;
 
+       if (unlikely(map_flags & BPF_F_LOCK)) {
+               if (unlikely(!map_value_has_spin_lock(map)))
+                       return -EINVAL;
+               /* find an element without taking the bucket lock */
+               l_old = lookup_nulls_elem_raw(head, hash, key, key_size,
+                                             htab->n_buckets);
+               ret = check_flags(htab, l_old, map_flags);
+               if (ret)
+                       return ret;
+               if (l_old) {
+                       /* grab the element lock and update value in place */
+                       copy_map_value_locked(map,
+                                             l_old->key + round_up(key_size, 8),
+                                             value, false);
+                       return 0;
+               }
+               /* fall through, grab the bucket lock and lookup again.
+                * 99.9% chance that the element won't be found,
+                * but second lookup under lock has to be done.
+                */
+       }
+
        /* bpf_map_update_elem() can be called in_irq() */
        raw_spin_lock_irqsave(&b->lock, flags);
 
@@ -850,6 +871,20 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
        if (ret)
                goto err;
 
+       if (unlikely(l_old && (map_flags & BPF_F_LOCK))) {
+               /* first lookup without the bucket lock didn't find the element,
+                * but second lookup with the bucket lock found it.
+                * This case is highly unlikely, but has to be dealt with:
+                * grab the element lock in addition to the bucket lock
+                * and update element in place
+                */
+               copy_map_value_locked(map,
+                                     l_old->key + round_up(key_size, 8),
+                                     value, false);
+               ret = 0;
+               goto err;
+       }
+
        l_new = alloc_htab_elem(htab, key, value, key_size, hash, false, false,
                                l_old);
        if (IS_ERR(l_new)) {
index a74972b..a411fc1 100644 (file)
@@ -221,6 +221,102 @@ const struct bpf_func_proto bpf_get_current_comm_proto = {
        .arg2_type      = ARG_CONST_SIZE,
 };
 
+#if defined(CONFIG_QUEUED_SPINLOCKS) || defined(CONFIG_BPF_ARCH_SPINLOCK)
+
+static inline void __bpf_spin_lock(struct bpf_spin_lock *lock)
+{
+       arch_spinlock_t *l = (void *)lock;
+       union {
+               __u32 val;
+               arch_spinlock_t lock;
+       } u = { .lock = __ARCH_SPIN_LOCK_UNLOCKED };
+
+       compiletime_assert(u.val == 0, "__ARCH_SPIN_LOCK_UNLOCKED not 0");
+       BUILD_BUG_ON(sizeof(*l) != sizeof(__u32));
+       BUILD_BUG_ON(sizeof(*lock) != sizeof(__u32));
+       arch_spin_lock(l);
+}
+
+static inline void __bpf_spin_unlock(struct bpf_spin_lock *lock)
+{
+       arch_spinlock_t *l = (void *)lock;
+
+       arch_spin_unlock(l);
+}
+
+#else
+
+static inline void __bpf_spin_lock(struct bpf_spin_lock *lock)
+{
+       atomic_t *l = (void *)lock;
+
+       BUILD_BUG_ON(sizeof(*l) != sizeof(*lock));
+       do {
+               atomic_cond_read_relaxed(l, !VAL);
+       } while (atomic_xchg(l, 1));
+}
+
+static inline void __bpf_spin_unlock(struct bpf_spin_lock *lock)
+{
+       atomic_t *l = (void *)lock;
+
+       atomic_set_release(l, 0);
+}
+
+#endif
+
+static DEFINE_PER_CPU(unsigned long, irqsave_flags);
+
+notrace BPF_CALL_1(bpf_spin_lock, struct bpf_spin_lock *, lock)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       __bpf_spin_lock(lock);
+       __this_cpu_write(irqsave_flags, flags);
+       return 0;
+}
+
+const struct bpf_func_proto bpf_spin_lock_proto = {
+       .func           = bpf_spin_lock,
+       .gpl_only       = false,
+       .ret_type       = RET_VOID,
+       .arg1_type      = ARG_PTR_TO_SPIN_LOCK,
+};
+
+notrace BPF_CALL_1(bpf_spin_unlock, struct bpf_spin_lock *, lock)
+{
+       unsigned long flags;
+
+       flags = __this_cpu_read(irqsave_flags);
+       __bpf_spin_unlock(lock);
+       local_irq_restore(flags);
+       return 0;
+}
+
+const struct bpf_func_proto bpf_spin_unlock_proto = {
+       .func           = bpf_spin_unlock,
+       .gpl_only       = false,
+       .ret_type       = RET_VOID,
+       .arg1_type      = ARG_PTR_TO_SPIN_LOCK,
+};
+
+void copy_map_value_locked(struct bpf_map *map, void *dst, void *src,
+                          bool lock_src)
+{
+       struct bpf_spin_lock *lock;
+
+       if (lock_src)
+               lock = src + map->spin_lock_off;
+       else
+               lock = dst + map->spin_lock_off;
+       preempt_disable();
+       ____bpf_spin_lock(lock);
+       copy_map_value(map, dst, src);
+       ____bpf_spin_unlock(lock);
+       preempt_enable();
+}
+
 #ifdef CONFIG_CGROUPS
 BPF_CALL_0(bpf_get_current_cgroup_id)
 {
index 07a34ef..6b572e2 100644 (file)
@@ -131,7 +131,14 @@ static int cgroup_storage_update_elem(struct bpf_map *map, void *_key,
        struct bpf_cgroup_storage *storage;
        struct bpf_storage_buffer *new;
 
-       if (flags != BPF_ANY && flags != BPF_EXIST)
+       if (unlikely(flags & ~(BPF_F_LOCK | BPF_EXIST | BPF_NOEXIST)))
+               return -EINVAL;
+
+       if (unlikely(flags & BPF_NOEXIST))
+               return -EINVAL;
+
+       if (unlikely((flags & BPF_F_LOCK) &&
+                    !map_value_has_spin_lock(map)))
                return -EINVAL;
 
        storage = cgroup_storage_lookup((struct bpf_cgroup_storage_map *)map,
@@ -139,6 +146,11 @@ static int cgroup_storage_update_elem(struct bpf_map *map, void *_key,
        if (!storage)
                return -ENOENT;
 
+       if (flags & BPF_F_LOCK) {
+               copy_map_value_locked(map, storage->buf->data, value, false);
+               return 0;
+       }
+
        new = kmalloc_node(sizeof(struct bpf_storage_buffer) +
                           map->value_size,
                           __GFP_ZERO | GFP_ATOMIC | __GFP_NOWARN,
@@ -147,6 +159,7 @@ static int cgroup_storage_update_elem(struct bpf_map *map, void *_key,
                return -ENOMEM;
 
        memcpy(&new->data[0], value, map->value_size);
+       check_and_init_map_lock(map, new->data);
 
        new = xchg(&storage->buf, new);
        kfree_rcu(new, rcu);
@@ -483,6 +496,7 @@ struct bpf_cgroup_storage *bpf_cgroup_storage_alloc(struct bpf_prog *prog,
                storage->buf = kmalloc_node(size, flags, map->numa_node);
                if (!storage->buf)
                        goto enomem;
+               check_and_init_map_lock(map, storage->buf->data);
        } else {
                storage->percpu_buf = __alloc_percpu_gfp(size, 8, flags);
                if (!storage->percpu_buf)
index 52378d3..583346a 100644 (file)
@@ -37,6 +37,11 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
                return ERR_PTR(-EINVAL);
        }
 
+       if (map_value_has_spin_lock(inner_map)) {
+               fdput(f);
+               return ERR_PTR(-ENOTSUPP);
+       }
+
        inner_map_meta_size = sizeof(*inner_map_meta);
        /* In some cases verifier needs to access beyond just base map. */
        if (inner_map->ops == &array_map_ops)
index 39dba8c..ba63520 100644 (file)
@@ -35,6 +35,7 @@ static DECLARE_RWSEM(bpf_devs_lock);
 struct bpf_offload_dev {
        const struct bpf_prog_offload_ops *ops;
        struct list_head netdevs;
+       void *priv;
 };
 
 struct bpf_offload_netdev {
@@ -669,7 +670,7 @@ unlock:
 EXPORT_SYMBOL_GPL(bpf_offload_dev_netdev_unregister);
 
 struct bpf_offload_dev *
-bpf_offload_dev_create(const struct bpf_prog_offload_ops *ops)
+bpf_offload_dev_create(const struct bpf_prog_offload_ops *ops, void *priv)
 {
        struct bpf_offload_dev *offdev;
        int err;
@@ -688,6 +689,7 @@ bpf_offload_dev_create(const struct bpf_prog_offload_ops *ops)
                return ERR_PTR(-ENOMEM);
 
        offdev->ops = ops;
+       offdev->priv = priv;
        INIT_LIST_HEAD(&offdev->netdevs);
 
        return offdev;
@@ -700,3 +702,9 @@ void bpf_offload_dev_destroy(struct bpf_offload_dev *offdev)
        kfree(offdev);
 }
 EXPORT_SYMBOL_GPL(bpf_offload_dev_destroy);
+
+void *bpf_offload_dev_priv(struct bpf_offload_dev *offdev)
+{
+       return offdev->priv;
+}
+EXPORT_SYMBOL_GPL(bpf_offload_dev_priv);
index 673fa6f..0c1b4ba 100644 (file)
@@ -28,8 +28,8 @@ void pcpu_freelist_destroy(struct pcpu_freelist *s)
        free_percpu(s->freelist);
 }
 
-static inline void __pcpu_freelist_push(struct pcpu_freelist_head *head,
-                                       struct pcpu_freelist_node *node)
+static inline void ___pcpu_freelist_push(struct pcpu_freelist_head *head,
+                                        struct pcpu_freelist_node *node)
 {
        raw_spin_lock(&head->lock);
        node->next = head->first;
@@ -37,12 +37,22 @@ static inline void __pcpu_freelist_push(struct pcpu_freelist_head *head,
        raw_spin_unlock(&head->lock);
 }
 
-void pcpu_freelist_push(struct pcpu_freelist *s,
+void __pcpu_freelist_push(struct pcpu_freelist *s,
                        struct pcpu_freelist_node *node)
 {
        struct pcpu_freelist_head *head = this_cpu_ptr(s->freelist);
 
-       __pcpu_freelist_push(head, node);
+       ___pcpu_freelist_push(head, node);
+}
+
+void pcpu_freelist_push(struct pcpu_freelist *s,
+                       struct pcpu_freelist_node *node)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       __pcpu_freelist_push(s, node);
+       local_irq_restore(flags);
 }
 
 void pcpu_freelist_populate(struct pcpu_freelist *s, void *buf, u32 elem_size,
@@ -63,7 +73,7 @@ void pcpu_freelist_populate(struct pcpu_freelist *s, void *buf, u32 elem_size,
        for_each_possible_cpu(cpu) {
 again:
                head = per_cpu_ptr(s->freelist, cpu);
-               __pcpu_freelist_push(head, buf);
+               ___pcpu_freelist_push(head, buf);
                i++;
                buf += elem_size;
                if (i == nr_elems)
@@ -74,14 +84,12 @@ again:
        local_irq_restore(flags);
 }
 
-struct pcpu_freelist_node *pcpu_freelist_pop(struct pcpu_freelist *s)
+struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *s)
 {
        struct pcpu_freelist_head *head;
        struct pcpu_freelist_node *node;
-       unsigned long flags;
        int orig_cpu, cpu;
 
-       local_irq_save(flags);
        orig_cpu = cpu = raw_smp_processor_id();
        while (1) {
                head = per_cpu_ptr(s->freelist, cpu);
@@ -89,16 +97,25 @@ struct pcpu_freelist_node *pcpu_freelist_pop(struct pcpu_freelist *s)
                node = head->first;
                if (node) {
                        head->first = node->next;
-                       raw_spin_unlock_irqrestore(&head->lock, flags);
+                       raw_spin_unlock(&head->lock);
                        return node;
                }
                raw_spin_unlock(&head->lock);
                cpu = cpumask_next(cpu, cpu_possible_mask);
                if (cpu >= nr_cpu_ids)
                        cpu = 0;
-               if (cpu == orig_cpu) {
-                       local_irq_restore(flags);
+               if (cpu == orig_cpu)
                        return NULL;
-               }
        }
 }
+
+struct pcpu_freelist_node *pcpu_freelist_pop(struct pcpu_freelist *s)
+{
+       struct pcpu_freelist_node *ret;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       ret = __pcpu_freelist_pop(s);
+       local_irq_restore(flags);
+       return ret;
+}
index 3049aae..c396011 100644 (file)
@@ -22,8 +22,12 @@ struct pcpu_freelist_node {
        struct pcpu_freelist_node *next;
 };
 
+/* pcpu_freelist_* do spin_lock_irqsave. */
 void pcpu_freelist_push(struct pcpu_freelist *, struct pcpu_freelist_node *);
 struct pcpu_freelist_node *pcpu_freelist_pop(struct pcpu_freelist *);
+/* __pcpu_freelist_* do spin_lock only. caller must disable irqs. */
+void __pcpu_freelist_push(struct pcpu_freelist *, struct pcpu_freelist_node *);
+struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *);
 void pcpu_freelist_populate(struct pcpu_freelist *s, void *buf, u32 elem_size,
                            u32 nr_elems);
 int pcpu_freelist_init(struct pcpu_freelist *);
index b155cd1..ec7c552 100644 (file)
@@ -463,7 +463,7 @@ int map_check_no_btf(const struct bpf_map *map,
        return -ENOTSUPP;
 }
 
-static int map_check_btf(const struct bpf_map *map, const struct btf *btf,
+static int map_check_btf(struct bpf_map *map, const struct btf *btf,
                         u32 btf_key_id, u32 btf_value_id)
 {
        const struct btf_type *key_type, *value_type;
@@ -478,6 +478,22 @@ static int map_check_btf(const struct bpf_map *map, const struct btf *btf,
        if (!value_type || value_size != map->value_size)
                return -EINVAL;
 
+       map->spin_lock_off = btf_find_spin_lock(btf, value_type);
+
+       if (map_value_has_spin_lock(map)) {
+               if (map->map_type != BPF_MAP_TYPE_HASH &&
+                   map->map_type != BPF_MAP_TYPE_ARRAY &&
+                   map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE)
+                       return -ENOTSUPP;
+               if (map->spin_lock_off + sizeof(struct bpf_spin_lock) >
+                   map->value_size) {
+                       WARN_ONCE(1,
+                                 "verifier bug spin_lock_off %d value_size %d\n",
+                                 map->spin_lock_off, map->value_size);
+                       return -EFAULT;
+               }
+       }
+
        if (map->ops->map_check_btf)
                ret = map->ops->map_check_btf(map, btf, key_type, value_type);
 
@@ -542,6 +558,8 @@ static int map_create(union bpf_attr *attr)
                map->btf = btf;
                map->btf_key_type_id = attr->btf_key_type_id;
                map->btf_value_type_id = attr->btf_value_type_id;
+       } else {
+               map->spin_lock_off = -EINVAL;
        }
 
        err = security_bpf_map_alloc(map);
@@ -664,7 +682,7 @@ static void *__bpf_copy_key(void __user *ukey, u64 key_size)
 }
 
 /* last field in 'union bpf_attr' used by this command */
-#define BPF_MAP_LOOKUP_ELEM_LAST_FIELD value
+#define BPF_MAP_LOOKUP_ELEM_LAST_FIELD flags
 
 static int map_lookup_elem(union bpf_attr *attr)
 {
@@ -680,6 +698,9 @@ static int map_lookup_elem(union bpf_attr *attr)
        if (CHECK_ATTR(BPF_MAP_LOOKUP_ELEM))
                return -EINVAL;
 
+       if (attr->flags & ~BPF_F_LOCK)
+               return -EINVAL;
+
        f = fdget(ufd);
        map = __bpf_map_get(f);
        if (IS_ERR(map))
@@ -690,6 +711,12 @@ static int map_lookup_elem(union bpf_attr *attr)
                goto err_put;
        }
 
+       if ((attr->flags & BPF_F_LOCK) &&
+           !map_value_has_spin_lock(map)) {
+               err = -EINVAL;
+               goto err_put;
+       }
+
        key = __bpf_copy_key(ukey, map->key_size);
        if (IS_ERR(key)) {
                err = PTR_ERR(key);
@@ -713,8 +740,13 @@ static int map_lookup_elem(union bpf_attr *attr)
 
        if (bpf_map_is_dev_bound(map)) {
                err = bpf_map_offload_lookup_elem(map, key, value);
-       } else if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||
-                  map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) {
+               goto done;
+       }
+
+       preempt_disable();
+       this_cpu_inc(bpf_prog_active);
+       if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||
+           map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) {
                err = bpf_percpu_hash_copy(map, key, value);
        } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) {
                err = bpf_percpu_array_copy(map, key, value);
@@ -740,11 +772,20 @@ static int map_lookup_elem(union bpf_attr *attr)
                        err = -ENOENT;
                } else {
                        err = 0;
-                       memcpy(value, ptr, value_size);
+                       if (attr->flags & BPF_F_LOCK)
+                               /* lock 'ptr' and copy everything but lock */
+                               copy_map_value_locked(map, value, ptr, true);
+                       else
+                               copy_map_value(map, value, ptr);
+                       /* mask lock, since value wasn't zero inited */
+                       check_and_init_map_lock(map, value);
                }
                rcu_read_unlock();
        }
+       this_cpu_dec(bpf_prog_active);
+       preempt_enable();
 
+done:
        if (err)
                goto free_value;
 
@@ -800,6 +841,12 @@ static int map_update_elem(union bpf_attr *attr)
                goto err_put;
        }
 
+       if ((attr->flags & BPF_F_LOCK) &&
+           !map_value_has_spin_lock(map)) {
+               err = -EINVAL;
+               goto err_put;
+       }
+
        key = __bpf_copy_key(ukey, map->key_size);
        if (IS_ERR(key)) {
                err = PTR_ERR(key);
index 8c1c21c..1b9496c 100644 (file)
@@ -213,6 +213,7 @@ struct bpf_call_arg_meta {
        s64 msize_smax_value;
        u64 msize_umax_value;
        int ptr_id;
+       int func_id;
 };
 
 static DEFINE_MUTEX(bpf_verifier_lock);
@@ -330,10 +331,19 @@ static bool type_is_pkt_pointer(enum bpf_reg_type type)
               type == PTR_TO_PACKET_META;
 }
 
+static bool type_is_sk_pointer(enum bpf_reg_type type)
+{
+       return type == PTR_TO_SOCKET ||
+               type == PTR_TO_SOCK_COMMON ||
+               type == PTR_TO_TCP_SOCK;
+}
+
 static bool reg_type_may_be_null(enum bpf_reg_type type)
 {
        return type == PTR_TO_MAP_VALUE_OR_NULL ||
-              type == PTR_TO_SOCKET_OR_NULL;
+              type == PTR_TO_SOCKET_OR_NULL ||
+              type == PTR_TO_SOCK_COMMON_OR_NULL ||
+              type == PTR_TO_TCP_SOCK_OR_NULL;
 }
 
 static bool type_is_refcounted(enum bpf_reg_type type)
@@ -351,6 +361,12 @@ static bool reg_is_refcounted(const struct bpf_reg_state *reg)
        return type_is_refcounted(reg->type);
 }
 
+static bool reg_may_point_to_spin_lock(const struct bpf_reg_state *reg)
+{
+       return reg->type == PTR_TO_MAP_VALUE &&
+               map_value_has_spin_lock(reg->map_ptr);
+}
+
 static bool reg_is_refcounted_or_null(const struct bpf_reg_state *reg)
 {
        return type_is_refcounted_or_null(reg->type);
@@ -370,6 +386,12 @@ static bool is_release_function(enum bpf_func_id func_id)
        return func_id == BPF_FUNC_sk_release;
 }
 
+static bool is_acquire_function(enum bpf_func_id func_id)
+{
+       return func_id == BPF_FUNC_sk_lookup_tcp ||
+               func_id == BPF_FUNC_sk_lookup_udp;
+}
+
 /* string representation of 'enum bpf_reg_type' */
 static const char * const reg_type_str[] = {
        [NOT_INIT]              = "?",
@@ -385,6 +407,10 @@ static const char * const reg_type_str[] = {
        [PTR_TO_FLOW_KEYS]      = "flow_keys",
        [PTR_TO_SOCKET]         = "sock",
        [PTR_TO_SOCKET_OR_NULL] = "sock_or_null",
+       [PTR_TO_SOCK_COMMON]    = "sock_common",
+       [PTR_TO_SOCK_COMMON_OR_NULL] = "sock_common_or_null",
+       [PTR_TO_TCP_SOCK]       = "tcp_sock",
+       [PTR_TO_TCP_SOCK_OR_NULL] = "tcp_sock_or_null",
 };
 
 static char slot_type_char[] = {
@@ -611,13 +637,10 @@ static int acquire_reference_state(struct bpf_verifier_env *env, int insn_idx)
 }
 
 /* release function corresponding to acquire_reference_state(). Idempotent. */
-static int __release_reference_state(struct bpf_func_state *state, int ptr_id)
+static int release_reference_state(struct bpf_func_state *state, int ptr_id)
 {
        int i, last_idx;
 
-       if (!ptr_id)
-               return -EFAULT;
-
        last_idx = state->acquired_refs - 1;
        for (i = 0; i < state->acquired_refs; i++) {
                if (state->refs[i].id == ptr_id) {
@@ -629,21 +652,7 @@ static int __release_reference_state(struct bpf_func_state *state, int ptr_id)
                        return 0;
                }
        }
-       return -EFAULT;
-}
-
-/* variation on the above for cases where we expect that there must be an
- * outstanding reference for the specified ptr_id.
- */
-static int release_reference_state(struct bpf_verifier_env *env, int ptr_id)
-{
-       struct bpf_func_state *state = cur_func(env);
-       int err;
-
-       err = __release_reference_state(state, ptr_id);
-       if (WARN_ON_ONCE(err != 0))
-               verbose(env, "verifier internal error: can't release reference\n");
-       return err;
+       return -EINVAL;
 }
 
 static int transfer_reference_state(struct bpf_func_state *dst,
@@ -712,6 +721,7 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state,
        }
        dst_state->speculative = src->speculative;
        dst_state->curframe = src->curframe;
+       dst_state->active_spin_lock = src->active_spin_lock;
        for (i = 0; i <= src->curframe; i++) {
                dst = dst_state->frame[i];
                if (!dst) {
@@ -1201,6 +1211,10 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
        case CONST_PTR_TO_MAP:
        case PTR_TO_SOCKET:
        case PTR_TO_SOCKET_OR_NULL:
+       case PTR_TO_SOCK_COMMON:
+       case PTR_TO_SOCK_COMMON_OR_NULL:
+       case PTR_TO_TCP_SOCK:
+       case PTR_TO_TCP_SOCK_OR_NULL:
                return true;
        default:
                return false;
@@ -1483,6 +1497,21 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno,
        if (err)
                verbose(env, "R%d max value is outside of the array range\n",
                        regno);
+
+       if (map_value_has_spin_lock(reg->map_ptr)) {
+               u32 lock = reg->map_ptr->spin_lock_off;
+
+               /* if any part of struct bpf_spin_lock can be touched by
+                * load/store reject this program.
+                * To check that [x1, x2) overlaps with [y1, y2)
+                * it is sufficient to check x1 < y2 && y1 < x2.
+                */
+               if (reg->smin_value + off < lock + sizeof(struct bpf_spin_lock) &&
+                    lock < reg->umax_value + off + size) {
+                       verbose(env, "bpf_spin_lock cannot be accessed directly by load/store\n");
+                       return -EACCES;
+               }
+       }
        return err;
 }
 
@@ -1617,12 +1646,14 @@ static int check_flow_keys_access(struct bpf_verifier_env *env, int off,
        return 0;
 }
 
-static int check_sock_access(struct bpf_verifier_env *env, u32 regno, int off,
-                            int size, enum bpf_access_type t)
+static int check_sock_access(struct bpf_verifier_env *env, int insn_idx,
+                            u32 regno, int off, int size,
+                            enum bpf_access_type t)
 {
        struct bpf_reg_state *regs = cur_regs(env);
        struct bpf_reg_state *reg = &regs[regno];
-       struct bpf_insn_access_aux info;
+       struct bpf_insn_access_aux info = {};
+       bool valid;
 
        if (reg->smin_value < 0) {
                verbose(env, "R%d min value is negative, either use unsigned index or do a if (index >=0) check.\n",
@@ -1630,13 +1661,31 @@ static int check_sock_access(struct bpf_verifier_env *env, u32 regno, int off,
                return -EACCES;
        }
 
-       if (!bpf_sock_is_valid_access(off, size, t, &info)) {
-               verbose(env, "invalid bpf_sock access off=%d size=%d\n",
-                       off, size);
-               return -EACCES;
+       switch (reg->type) {
+       case PTR_TO_SOCK_COMMON:
+               valid = bpf_sock_common_is_valid_access(off, size, t, &info);
+               break;
+       case PTR_TO_SOCKET:
+               valid = bpf_sock_is_valid_access(off, size, t, &info);
+               break;
+       case PTR_TO_TCP_SOCK:
+               valid = bpf_tcp_sock_is_valid_access(off, size, t, &info);
+               break;
+       default:
+               valid = false;
        }
 
-       return 0;
+
+       if (valid) {
+               env->insn_aux_data[insn_idx].ctx_field_size =
+                       info.ctx_field_size;
+               return 0;
+       }
+
+       verbose(env, "R%d invalid %s access off=%d size=%d\n",
+               regno, reg_type_str[reg->type], off, size);
+
+       return -EACCES;
 }
 
 static bool __is_pointer_value(bool allow_ptr_leaks,
@@ -1662,8 +1711,14 @@ static bool is_ctx_reg(struct bpf_verifier_env *env, int regno)
 {
        const struct bpf_reg_state *reg = reg_state(env, regno);
 
-       return reg->type == PTR_TO_CTX ||
-              reg->type == PTR_TO_SOCKET;
+       return reg->type == PTR_TO_CTX;
+}
+
+static bool is_sk_reg(struct bpf_verifier_env *env, int regno)
+{
+       const struct bpf_reg_state *reg = reg_state(env, regno);
+
+       return type_is_sk_pointer(reg->type);
 }
 
 static bool is_pkt_reg(struct bpf_verifier_env *env, int regno)
@@ -1774,6 +1829,12 @@ static int check_ptr_alignment(struct bpf_verifier_env *env,
        case PTR_TO_SOCKET:
                pointer_desc = "sock ";
                break;
+       case PTR_TO_SOCK_COMMON:
+               pointer_desc = "sock_common ";
+               break;
+       case PTR_TO_TCP_SOCK:
+               pointer_desc = "tcp_sock ";
+               break;
        default:
                break;
        }
@@ -1977,11 +2038,14 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
                         * PTR_TO_PACKET[_META,_END]. In the latter
                         * case, we know the offset is zero.
                         */
-                       if (reg_type == SCALAR_VALUE)
+                       if (reg_type == SCALAR_VALUE) {
                                mark_reg_unknown(env, regs, value_regno);
-                       else
+                       } else {
                                mark_reg_known_zero(env, regs,
                                                    value_regno);
+                               if (reg_type_may_be_null(reg_type))
+                                       regs[value_regno].id = ++env->id_gen;
+                       }
                        regs[value_regno].type = reg_type;
                }
 
@@ -2027,12 +2091,13 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
                err = check_flow_keys_access(env, off, size);
                if (!err && t == BPF_READ && value_regno >= 0)
                        mark_reg_unknown(env, regs, value_regno);
-       } else if (reg->type == PTR_TO_SOCKET) {
+       } else if (type_is_sk_pointer(reg->type)) {
                if (t == BPF_WRITE) {
-                       verbose(env, "cannot write into socket\n");
+                       verbose(env, "R%d cannot write into %s\n",
+                               regno, reg_type_str[reg->type]);
                        return -EACCES;
                }
-               err = check_sock_access(env, regno, off, size, t);
+               err = check_sock_access(env, insn_idx, regno, off, size, t);
                if (!err && value_regno >= 0)
                        mark_reg_unknown(env, regs, value_regno);
        } else {
@@ -2076,7 +2141,8 @@ static int check_xadd(struct bpf_verifier_env *env, int insn_idx, struct bpf_ins
 
        if (is_ctx_reg(env, insn->dst_reg) ||
            is_pkt_reg(env, insn->dst_reg) ||
-           is_flow_key_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",
                        insn->dst_reg,
                        reg_type_str[reg_state(env, insn->dst_reg)->type]);
@@ -2192,6 +2258,91 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
        }
 }
 
+/* Implementation details:
+ * bpf_map_lookup returns PTR_TO_MAP_VALUE_OR_NULL
+ * Two bpf_map_lookups (even with the same key) will have different reg->id.
+ * For traditional PTR_TO_MAP_VALUE the verifier clears reg->id after
+ * value_or_null->value transition, since the verifier only cares about
+ * the range of access to valid map value pointer and doesn't care about actual
+ * address of the map element.
+ * For maps with 'struct bpf_spin_lock' inside map value the verifier keeps
+ * reg->id > 0 after value_or_null->value transition. By doing so
+ * two bpf_map_lookups will be considered two different pointers that
+ * point to different bpf_spin_locks.
+ * The verifier allows taking only one bpf_spin_lock at a time to avoid
+ * dead-locks.
+ * Since only one bpf_spin_lock is allowed the checks are simpler than
+ * reg_is_refcounted() logic. The verifier needs to remember only
+ * one spin_lock instead of array of acquired_refs.
+ * cur_state->active_spin_lock remembers which map value element got locked
+ * and clears it after bpf_spin_unlock.
+ */
+static int process_spin_lock(struct bpf_verifier_env *env, int regno,
+                            bool is_lock)
+{
+       struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
+       struct bpf_verifier_state *cur = env->cur_state;
+       bool is_const = tnum_is_const(reg->var_off);
+       struct bpf_map *map = reg->map_ptr;
+       u64 val = reg->var_off.value;
+
+       if (reg->type != PTR_TO_MAP_VALUE) {
+               verbose(env, "R%d is not a pointer to map_value\n", regno);
+               return -EINVAL;
+       }
+       if (!is_const) {
+               verbose(env,
+                       "R%d doesn't have constant offset. bpf_spin_lock has to be at the constant offset\n",
+                       regno);
+               return -EINVAL;
+       }
+       if (!map->btf) {
+               verbose(env,
+                       "map '%s' has to have BTF in order to use bpf_spin_lock\n",
+                       map->name);
+               return -EINVAL;
+       }
+       if (!map_value_has_spin_lock(map)) {
+               if (map->spin_lock_off == -E2BIG)
+                       verbose(env,
+                               "map '%s' has more than one 'struct bpf_spin_lock'\n",
+                               map->name);
+               else if (map->spin_lock_off == -ENOENT)
+                       verbose(env,
+                               "map '%s' doesn't have 'struct bpf_spin_lock'\n",
+                               map->name);
+               else
+                       verbose(env,
+                               "map '%s' is not a struct type or bpf_spin_lock is mangled\n",
+                               map->name);
+               return -EINVAL;
+       }
+       if (map->spin_lock_off != val + reg->off) {
+               verbose(env, "off %lld doesn't point to 'struct bpf_spin_lock'\n",
+                       val + reg->off);
+               return -EINVAL;
+       }
+       if (is_lock) {
+               if (cur->active_spin_lock) {
+                       verbose(env,
+                               "Locking two bpf_spin_locks are not allowed\n");
+                       return -EINVAL;
+               }
+               cur->active_spin_lock = reg->id;
+       } else {
+               if (!cur->active_spin_lock) {
+                       verbose(env, "bpf_spin_unlock without taking a lock\n");
+                       return -EINVAL;
+               }
+               if (cur->active_spin_lock != reg->id) {
+                       verbose(env, "bpf_spin_unlock of different lock\n");
+                       return -EINVAL;
+               }
+               cur->active_spin_lock = 0;
+       }
+       return 0;
+}
+
 static bool arg_type_is_mem_ptr(enum bpf_arg_type type)
 {
        return type == ARG_PTR_TO_MEM ||
@@ -2258,6 +2409,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
                err = check_ctx_reg(env, reg, regno);
                if (err < 0)
                        return err;
+       } else if (arg_type == ARG_PTR_TO_SOCK_COMMON) {
+               expected_type = PTR_TO_SOCK_COMMON;
+               /* Any sk pointer can be ARG_PTR_TO_SOCK_COMMON */
+               if (!type_is_sk_pointer(type))
+                       goto err_type;
        } else if (arg_type == ARG_PTR_TO_SOCKET) {
                expected_type = PTR_TO_SOCKET;
                if (type != expected_type)
@@ -2268,6 +2424,17 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
                        return -EFAULT;
                }
                meta->ptr_id = reg->id;
+       } else if (arg_type == ARG_PTR_TO_SPIN_LOCK) {
+               if (meta->func_id == BPF_FUNC_spin_lock) {
+                       if (process_spin_lock(env, regno, true))
+                               return -EACCES;
+               } else if (meta->func_id == BPF_FUNC_spin_unlock) {
+                       if (process_spin_lock(env, regno, false))
+                               return -EACCES;
+               } else {
+                       verbose(env, "verifier internal error\n");
+                       return -EFAULT;
+               }
        } else if (arg_type_is_mem_ptr(arg_type)) {
                expected_type = PTR_TO_STACK;
                /* One exception here. In case function allows for NULL to be
@@ -2661,7 +2828,7 @@ static int release_reference(struct bpf_verifier_env *env,
        for (i = 0; i <= vstate->curframe; i++)
                release_reg_references(env, vstate->frame[i], meta->ptr_id);
 
-       return release_reference_state(env, meta->ptr_id);
+       return release_reference_state(cur_func(env), meta->ptr_id);
 }
 
 static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
@@ -2887,6 +3054,7 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
                return err;
        }
 
+       meta.func_id = func_id;
        /* check args */
        err = check_func_arg(env, BPF_REG_1, fn->arg1_type, &meta);
        if (err)
@@ -2926,8 +3094,11 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
                }
        } else if (is_release_function(func_id)) {
                err = release_reference(env, &meta);
-               if (err)
+               if (err) {
+                       verbose(env, "func %s#%d reference has not been acquired before\n",
+                               func_id_name(func_id), func_id);
                        return err;
+               }
        }
 
        regs = cur_regs(env);
@@ -2969,17 +3140,30 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
                regs[BPF_REG_0].map_ptr = meta.map_ptr;
                if (fn->ret_type == RET_PTR_TO_MAP_VALUE) {
                        regs[BPF_REG_0].type = PTR_TO_MAP_VALUE;
+                       if (map_value_has_spin_lock(meta.map_ptr))
+                               regs[BPF_REG_0].id = ++env->id_gen;
                } else {
                        regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL;
                        regs[BPF_REG_0].id = ++env->id_gen;
                }
        } else if (fn->ret_type == RET_PTR_TO_SOCKET_OR_NULL) {
-               int id = acquire_reference_state(env, insn_idx);
-               if (id < 0)
-                       return id;
                mark_reg_known_zero(env, regs, BPF_REG_0);
                regs[BPF_REG_0].type = PTR_TO_SOCKET_OR_NULL;
-               regs[BPF_REG_0].id = id;
+               if (is_acquire_function(func_id)) {
+                       int id = acquire_reference_state(env, insn_idx);
+
+                       if (id < 0)
+                               return id;
+                       /* For release_reference() */
+                       regs[BPF_REG_0].id = id;
+               } else {
+                       /* For mark_ptr_or_null_reg() */
+                       regs[BPF_REG_0].id = ++env->id_gen;
+               }
+       } else if (fn->ret_type == RET_PTR_TO_TCP_SOCK_OR_NULL) {
+               mark_reg_known_zero(env, regs, BPF_REG_0);
+               regs[BPF_REG_0].type = PTR_TO_TCP_SOCK_OR_NULL;
+               regs[BPF_REG_0].id = ++env->id_gen;
        } else {
                verbose(env, "unknown return type %d of func %s#%d\n",
                        fn->ret_type, func_id_name(func_id), func_id);
@@ -3239,6 +3423,10 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
        case PTR_TO_PACKET_END:
        case PTR_TO_SOCKET:
        case PTR_TO_SOCKET_OR_NULL:
+       case PTR_TO_SOCK_COMMON:
+       case PTR_TO_SOCK_COMMON_OR_NULL:
+       case PTR_TO_TCP_SOCK:
+       case PTR_TO_TCP_SOCK_OR_NULL:
                verbose(env, "R%d pointer arithmetic on %s prohibited\n",
                        dst, reg_type_str[ptr_reg->type]);
                return -EACCES;
@@ -4472,8 +4660,13 @@ static void mark_ptr_or_null_reg(struct bpf_func_state *state,
                        }
                } else if (reg->type == PTR_TO_SOCKET_OR_NULL) {
                        reg->type = PTR_TO_SOCKET;
+               } else if (reg->type == PTR_TO_SOCK_COMMON_OR_NULL) {
+                       reg->type = PTR_TO_SOCK_COMMON;
+               } else if (reg->type == PTR_TO_TCP_SOCK_OR_NULL) {
+                       reg->type = PTR_TO_TCP_SOCK;
                }
-               if (is_null || !reg_is_refcounted(reg)) {
+               if (is_null || !(reg_is_refcounted(reg) ||
+                                reg_may_point_to_spin_lock(reg))) {
                        /* We don't need id from this point onwards anymore,
                         * thus we should better reset it, so that state
                         * pruning has chances to take effect.
@@ -4495,7 +4688,7 @@ static void mark_ptr_or_null_regs(struct bpf_verifier_state *vstate, u32 regno,
        int i, j;
 
        if (reg_is_refcounted_or_null(&regs[regno]) && is_null)
-               __release_reference_state(state, id);
+               release_reference_state(state, id);
 
        for (i = 0; i < MAX_BPF_REG; i++)
                mark_ptr_or_null_reg(state, &regs[i], id, is_null);
@@ -4871,6 +5064,11 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
                return err;
        }
 
+       if (env->cur_state->active_spin_lock) {
+               verbose(env, "BPF_LD_[ABS|IND] cannot be used inside bpf_spin_lock-ed region\n");
+               return -EINVAL;
+       }
+
        if (regs[BPF_REG_6].type != PTR_TO_CTX) {
                verbose(env,
                        "at the time of BPF_LD_ABS|IND R6 != pointer to skb\n");
@@ -5607,8 +5805,11 @@ static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur,
        case PTR_TO_MAP_VALUE:
                /* If the new min/max/var_off satisfy the old ones and
                 * everything else matches, we are OK.
-                * We don't care about the 'id' value, because nothing
-                * uses it for PTR_TO_MAP_VALUE (only for ..._OR_NULL)
+                * 'id' is not compared, since it's only used for maps with
+                * bpf_spin_lock inside map element and in such cases if
+                * the rest of the prog is valid for one map element then
+                * it's valid for all map elements regardless of the key
+                * used in bpf_map_lookup()
                 */
                return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 &&
                       range_within(rold, rcur) &&
@@ -5656,6 +5857,10 @@ static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur,
        case PTR_TO_FLOW_KEYS:
        case PTR_TO_SOCKET:
        case PTR_TO_SOCKET_OR_NULL:
+       case PTR_TO_SOCK_COMMON:
+       case PTR_TO_SOCK_COMMON_OR_NULL:
+       case PTR_TO_TCP_SOCK:
+       case PTR_TO_TCP_SOCK_OR_NULL:
                /* Only valid matches are exact, which memcmp() above
                 * would have accepted
                 */
@@ -5811,6 +6016,9 @@ static bool states_equal(struct bpf_verifier_env *env,
        if (old->speculative && !cur->speculative)
                return false;
 
+       if (old->active_spin_lock != cur->active_spin_lock)
+               return false;
+
        /* for states to be equal callsites have to be the same
         * and all frame states need to be equivalent
         */
@@ -5973,6 +6181,10 @@ static bool reg_type_mismatch_ok(enum bpf_reg_type type)
        case PTR_TO_CTX:
        case PTR_TO_SOCKET:
        case PTR_TO_SOCKET_OR_NULL:
+       case PTR_TO_SOCK_COMMON:
+       case PTR_TO_SOCK_COMMON_OR_NULL:
+       case PTR_TO_TCP_SOCK:
+       case PTR_TO_TCP_SOCK_OR_NULL:
                return false;
        default:
                return true;
@@ -6229,6 +6441,12 @@ static int do_check(struct bpf_verifier_env *env)
                                        return -EINVAL;
                                }
 
+                               if (env->cur_state->active_spin_lock &&
+                                   (insn->src_reg == BPF_PSEUDO_CALL ||
+                                    insn->imm != BPF_FUNC_spin_unlock)) {
+                                       verbose(env, "function calls are not allowed while holding a lock\n");
+                                       return -EINVAL;
+                               }
                                if (insn->src_reg == BPF_PSEUDO_CALL)
                                        err = check_func_call(env, insn, &env->insn_idx);
                                else
@@ -6259,6 +6477,11 @@ static int do_check(struct bpf_verifier_env *env)
                                        return -EINVAL;
                                }
 
+                               if (env->cur_state->active_spin_lock) {
+                                       verbose(env, "bpf_spin_unlock is missing\n");
+                                       return -EINVAL;
+                               }
+
                                if (state->curframe) {
                                        /* exit from nested function */
                                        env->prev_insn_idx = env->insn_idx;
@@ -6356,6 +6579,19 @@ static int check_map_prealloc(struct bpf_map *map)
                !(map->map_flags & BPF_F_NO_PREALLOC);
 }
 
+static bool is_tracing_prog_type(enum bpf_prog_type type)
+{
+       switch (type) {
+       case BPF_PROG_TYPE_KPROBE:
+       case BPF_PROG_TYPE_TRACEPOINT:
+       case BPF_PROG_TYPE_PERF_EVENT:
+       case BPF_PROG_TYPE_RAW_TRACEPOINT:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static int check_map_prog_compatibility(struct bpf_verifier_env *env,
                                        struct bpf_map *map,
                                        struct bpf_prog *prog)
@@ -6378,6 +6614,13 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
                }
        }
 
+       if ((is_tracing_prog_type(prog->type) ||
+            prog->type == BPF_PROG_TYPE_SOCKET_FILTER) &&
+           map_value_has_spin_lock(map)) {
+               verbose(env, "tracing progs cannot use bpf_spin_lock yet\n");
+               return -EINVAL;
+       }
+
        if ((bpf_prog_is_dev_bound(prog->aux) || bpf_map_is_dev_bound(map)) &&
            !bpf_offload_prog_map_match(prog, map)) {
                verbose(env, "offload device mismatch between prog and map\n");
@@ -6944,8 +7187,12 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
                        convert_ctx_access = ops->convert_ctx_access;
                        break;
                case PTR_TO_SOCKET:
+               case PTR_TO_SOCK_COMMON:
                        convert_ctx_access = bpf_sock_convert_ctx_access;
                        break;
+               case PTR_TO_TCP_SOCK:
+                       convert_ctx_access = bpf_tcp_sock_convert_ctx_access;
+                       break;
                default:
                        continue;
                }
index f31bd61..9f61760 100644 (file)
@@ -5996,7 +5996,7 @@ int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
        int ret;
 
        mutex_lock(&cgroup_mutex);
-       ret = __cgroup_bpf_detach(cgrp, prog, type, flags);
+       ret = __cgroup_bpf_detach(cgrp, prog, type);
        mutex_unlock(&cgroup_mutex);
        return ret;
 }
index 91d5c38..d1c6d15 100644 (file)
@@ -376,9 +376,6 @@ void __weak arch_smt_update(void) { }
 
 #ifdef CONFIG_HOTPLUG_SMT
 enum cpuhp_smt_control cpu_smt_control __read_mostly = CPU_SMT_ENABLED;
-EXPORT_SYMBOL_GPL(cpu_smt_control);
-
-static bool cpu_smt_available __read_mostly;
 
 void __init cpu_smt_disable(bool force)
 {
@@ -397,25 +394,11 @@ void __init cpu_smt_disable(bool force)
 
 /*
  * The decision whether SMT is supported can only be done after the full
- * CPU identification. Called from architecture code before non boot CPUs
- * are brought up.
- */
-void __init cpu_smt_check_topology_early(void)
-{
-       if (!topology_smt_supported())
-               cpu_smt_control = CPU_SMT_NOT_SUPPORTED;
-}
-
-/*
- * If SMT was disabled by BIOS, detect it here, after the CPUs have been
- * brought online. This ensures the smt/l1tf sysfs entries are consistent
- * with reality. cpu_smt_available is set to true during the bringup of non
- * boot CPUs when a SMT sibling is detected. Note, this may overwrite
- * cpu_smt_control's previous setting.
+ * CPU identification. Called from architecture code.
  */
 void __init cpu_smt_check_topology(void)
 {
-       if (!cpu_smt_available)
+       if (!topology_smt_supported())
                cpu_smt_control = CPU_SMT_NOT_SUPPORTED;
 }
 
@@ -428,18 +411,10 @@ early_param("nosmt", smt_cmdline_disable);
 
 static inline bool cpu_smt_allowed(unsigned int cpu)
 {
-       if (topology_is_primary_thread(cpu))
+       if (cpu_smt_control == CPU_SMT_ENABLED)
                return true;
 
-       /*
-        * If the CPU is not a 'primary' thread and the booted_once bit is
-        * set then the processor has SMT support. Store this information
-        * for the late check of SMT support in cpu_smt_check_topology().
-        */
-       if (per_cpu(cpuhp_state, cpu).booted_once)
-               cpu_smt_available = true;
-
-       if (cpu_smt_control == CPU_SMT_ENABLED)
+       if (topology_is_primary_thread(cpu))
                return true;
 
        /*
@@ -2090,10 +2065,8 @@ static int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval)
                 */
                cpuhp_offline_cpu_device(cpu);
        }
-       if (!ret) {
+       if (!ret)
                cpu_smt_control = ctrlval;
-               arch_smt_update();
-       }
        cpu_maps_update_done();
        return ret;
 }
@@ -2104,7 +2077,6 @@ static int cpuhp_smt_enable(void)
 
        cpu_maps_update_begin();
        cpu_smt_control = CPU_SMT_ENABLED;
-       arch_smt_update();
        for_each_present_cpu(cpu) {
                /* Skip online CPUs and CPUs on offline nodes */
                if (cpu_online(cpu) || !node_online(cpu_to_node(cpu)))
index 3cd13a3..e5ede69 100644 (file)
@@ -436,18 +436,18 @@ int perf_proc_update_handler(struct ctl_table *table, int write,
                void __user *buffer, size_t *lenp,
                loff_t *ppos)
 {
-       int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
-
-       if (ret || !write)
-               return ret;
-
+       int ret;
+       int perf_cpu = sysctl_perf_cpu_time_max_percent;
        /*
         * If throttling is disabled don't allow the write:
         */
-       if (sysctl_perf_cpu_time_max_percent == 100 ||
-           sysctl_perf_cpu_time_max_percent == 0)
+       if (write && (perf_cpu == 100 || perf_cpu == 0))
                return -EINVAL;
 
+       ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+       if (ret || !write)
+               return ret;
+
        max_samples_per_tick = DIV_ROUND_UP(sysctl_perf_event_sample_rate, HZ);
        perf_sample_period_ns = NSEC_PER_SEC / sysctl_perf_event_sample_rate;
        update_perf_cpu_limits();
index 4a99370..309ef5a 100644 (file)
@@ -734,6 +734,9 @@ struct ring_buffer *rb_alloc(int nr_pages, long watermark, int cpu, int flags)
        size = sizeof(struct ring_buffer);
        size += nr_pages * sizeof(void *);
 
+       if (order_base_2(size) >= MAX_ORDER)
+               goto fail;
+
        rb = kzalloc(size, GFP_KERNEL);
        if (!rb)
                goto fail;
index 284f2fe..2639a30 100644 (file)
@@ -307,7 +307,7 @@ void rcuwait_wake_up(struct rcuwait *w)
         *        MB (A)              MB (B)
         *    [L] cond            [L] tsk
         */
-       smp_rmb(); /* (B) */
+       smp_mb(); /* (B) */
 
        /*
         * Avoid using task_rcu_dereference() magic as long as we are careful,
@@ -558,12 +558,14 @@ static struct task_struct *find_alive_thread(struct task_struct *p)
        return NULL;
 }
 
-static struct task_struct *find_child_reaper(struct task_struct *father)
+static struct task_struct *find_child_reaper(struct task_struct *father,
+                                               struct list_head *dead)
        __releases(&tasklist_lock)
        __acquires(&tasklist_lock)
 {
        struct pid_namespace *pid_ns = task_active_pid_ns(father);
        struct task_struct *reaper = pid_ns->child_reaper;
+       struct task_struct *p, *n;
 
        if (likely(reaper != father))
                return reaper;
@@ -579,6 +581,12 @@ static struct task_struct *find_child_reaper(struct task_struct *father)
                panic("Attempted to kill init! exitcode=0x%08x\n",
                        father->signal->group_exit_code ?: father->exit_code);
        }
+
+       list_for_each_entry_safe(p, n, dead, ptrace_entry) {
+               list_del_init(&p->ptrace_entry);
+               release_task(p);
+       }
+
        zap_pid_ns_processes(pid_ns);
        write_lock_irq(&tasklist_lock);
 
@@ -668,7 +676,7 @@ static void forget_original_parent(struct task_struct *father,
                exit_ptrace(father, dead);
 
        /* Can drop and reacquire tasklist_lock */
-       reaper = find_child_reaper(father);
+       reaper = find_child_reaper(father, dead);
        if (list_empty(&father->children))
                return;
 
index be3bff2..a0514e0 100644 (file)
@@ -1452,11 +1452,7 @@ static void mark_wake_futex(struct wake_q_head *wake_q, struct futex_q *q)
        if (WARN(q->pi_state || q->rt_waiter, "refusing to wake PI futex\n"))
                return;
 
-       /*
-        * Queue the task for later wakeup for after we've released
-        * the hb->lock. wake_q_add() grabs reference to p.
-        */
-       wake_q_add(wake_q, p);
+       get_task_struct(p);
        __unqueue_futex(q);
        /*
         * The waiting task can free the futex_q as soon as q->lock_ptr = NULL
@@ -1466,6 +1462,13 @@ static void mark_wake_futex(struct wake_q_head *wake_q, struct futex_q *q)
         * plist_del in __unqueue_futex().
         */
        smp_store_release(&q->lock_ptr, NULL);
+
+       /*
+        * Queue the task for later wakeup for after we've released
+        * the hb->lock. wake_q_add() grabs reference to p.
+        */
+       wake_q_add(wake_q, p);
+       put_task_struct(p);
 }
 
 /*
@@ -2218,11 +2221,11 @@ static inline struct futex_hash_bucket *queue_lock(struct futex_q *q)
         * decrement the counter at queue_unlock() when some error has
         * occurred and we don't end up adding the task to the list.
         */
-       hb_waiters_inc(hb);
+       hb_waiters_inc(hb); /* implies smp_mb(); (A) */
 
        q->lock_ptr = &hb->lock;
 
-       spin_lock(&hb->lock); /* implies smp_mb(); (A) */
+       spin_lock(&hb->lock);
        return hb;
 }
 
@@ -2858,35 +2861,39 @@ retry_private:
         * and BUG when futex_unlock_pi() interleaves with this.
         *
         * Therefore acquire wait_lock while holding hb->lock, but drop the
-        * latter before calling rt_mutex_start_proxy_lock(). This still fully
-        * serializes against futex_unlock_pi() as that does the exact same
-        * lock handoff sequence.
+        * latter before calling __rt_mutex_start_proxy_lock(). This
+        * interleaves with futex_unlock_pi() -- which does a similar lock
+        * handoff -- such that the latter can observe the futex_q::pi_state
+        * before __rt_mutex_start_proxy_lock() is done.
         */
        raw_spin_lock_irq(&q.pi_state->pi_mutex.wait_lock);
        spin_unlock(q.lock_ptr);
+       /*
+        * __rt_mutex_start_proxy_lock() unconditionally enqueues the @rt_waiter
+        * such that futex_unlock_pi() is guaranteed to observe the waiter when
+        * it sees the futex_q::pi_state.
+        */
        ret = __rt_mutex_start_proxy_lock(&q.pi_state->pi_mutex, &rt_waiter, current);
        raw_spin_unlock_irq(&q.pi_state->pi_mutex.wait_lock);
 
        if (ret) {
                if (ret == 1)
                        ret = 0;
-
-               spin_lock(q.lock_ptr);
-               goto no_block;
+               goto cleanup;
        }
 
-
        if (unlikely(to))
                hrtimer_start_expires(&to->timer, HRTIMER_MODE_ABS);
 
        ret = rt_mutex_wait_proxy_lock(&q.pi_state->pi_mutex, to, &rt_waiter);
 
+cleanup:
        spin_lock(q.lock_ptr);
        /*
-        * If we failed to acquire the lock (signal/timeout), we must
+        * If we failed to acquire the lock (deadlock/signal/timeout), we must
         * first acquire the hb->lock before removing the lock from the
-        * rt_mutex waitqueue, such that we can keep the hb and rt_mutex
-        * wait lists consistent.
+        * rt_mutex waitqueue, such that we can keep the hb and rt_mutex wait
+        * lists consistent.
         *
         * In particular; it is important that futex_unlock_pi() can not
         * observe this inconsistency.
@@ -3010,6 +3017,10 @@ retry:
                 * there is no point where we hold neither; and therefore
                 * wake_futex_pi() must observe a state consistent with what we
                 * observed.
+                *
+                * In particular; this forces __rt_mutex_start_proxy() to
+                * complete such that we're guaranteed to observe the
+                * rt_waiter. Also see the WARN in wake_futex_pi().
                 */
                raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
                spin_unlock(&hb->lock);
index ee062b7..ef8ad36 100644 (file)
@@ -457,7 +457,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
 
        /* Validate affinity mask(s) */
        if (affinity) {
-               for (i = 0; i < cnt; i++, i++) {
+               for (i = 0; i < cnt; i++) {
                        if (cpumask_empty(&affinity[i].mask))
                                return -EINVAL;
                }
index a4888ce..84b54a1 100644 (file)
@@ -393,6 +393,9 @@ int irq_setup_affinity(struct irq_desc *desc)
        }
 
        cpumask_and(&mask, cpu_online_mask, set);
+       if (cpumask_empty(&mask))
+               cpumask_copy(&mask, cpu_online_mask);
+
        if (node != NUMA_NO_NODE) {
                const struct cpumask *nodemask = cpumask_of_node(node);
 
index 581edcc..978d63a 100644 (file)
@@ -1726,12 +1726,33 @@ void rt_mutex_proxy_unlock(struct rt_mutex *lock,
        rt_mutex_set_owner(lock, NULL);
 }
 
+/**
+ * __rt_mutex_start_proxy_lock() - Start lock acquisition for another task
+ * @lock:              the rt_mutex to take
+ * @waiter:            the pre-initialized rt_mutex_waiter
+ * @task:              the task to prepare
+ *
+ * Starts the rt_mutex acquire; it enqueues the @waiter and does deadlock
+ * detection. It does not wait, see rt_mutex_wait_proxy_lock() for that.
+ *
+ * NOTE: does _NOT_ remove the @waiter on failure; must either call
+ * rt_mutex_wait_proxy_lock() or rt_mutex_cleanup_proxy_lock() after this.
+ *
+ * Returns:
+ *  0 - task blocked on lock
+ *  1 - acquired the lock for task, caller should wake it up
+ * <0 - error
+ *
+ * Special API call for PI-futex support.
+ */
 int __rt_mutex_start_proxy_lock(struct rt_mutex *lock,
                              struct rt_mutex_waiter *waiter,
                              struct task_struct *task)
 {
        int ret;
 
+       lockdep_assert_held(&lock->wait_lock);
+
        if (try_to_take_rt_mutex(lock, task, NULL))
                return 1;
 
@@ -1749,9 +1770,6 @@ int __rt_mutex_start_proxy_lock(struct rt_mutex *lock,
                ret = 0;
        }
 
-       if (unlikely(ret))
-               remove_waiter(lock, waiter);
-
        debug_rt_mutex_print_deadlock(waiter);
 
        return ret;
@@ -1763,12 +1781,18 @@ int __rt_mutex_start_proxy_lock(struct rt_mutex *lock,
  * @waiter:            the pre-initialized rt_mutex_waiter
  * @task:              the task to prepare
  *
+ * Starts the rt_mutex acquire; it enqueues the @waiter and does deadlock
+ * detection. It does not wait, see rt_mutex_wait_proxy_lock() for that.
+ *
+ * NOTE: unlike __rt_mutex_start_proxy_lock this _DOES_ remove the @waiter
+ * on failure.
+ *
  * Returns:
  *  0 - task blocked on lock
  *  1 - acquired the lock for task, caller should wake it up
  * <0 - error
  *
- * Special API call for FUTEX_REQUEUE_PI support.
+ * Special API call for PI-futex support.
  */
 int rt_mutex_start_proxy_lock(struct rt_mutex *lock,
                              struct rt_mutex_waiter *waiter,
@@ -1778,6 +1802,8 @@ int rt_mutex_start_proxy_lock(struct rt_mutex *lock,
 
        raw_spin_lock_irq(&lock->wait_lock);
        ret = __rt_mutex_start_proxy_lock(lock, waiter, task);
+       if (unlikely(ret))
+               remove_waiter(lock, waiter);
        raw_spin_unlock_irq(&lock->wait_lock);
 
        return ret;
@@ -1845,7 +1871,8 @@ int rt_mutex_wait_proxy_lock(struct rt_mutex *lock,
  * @lock:              the rt_mutex we were woken on
  * @waiter:            the pre-initialized rt_mutex_waiter
  *
- * Attempt to clean up after a failed rt_mutex_wait_proxy_lock().
+ * Attempt to clean up after a failed __rt_mutex_start_proxy_lock() or
+ * rt_mutex_wait_proxy_lock().
  *
  * Unless we acquired the lock; we're still enqueued on the wait-list and can
  * in fact still be granted ownership until we're removed. Therefore we can
index 09b1800..50d9af6 100644 (file)
@@ -198,15 +198,22 @@ static void __rwsem_mark_wake(struct rw_semaphore *sem,
                woken++;
                tsk = waiter->task;
 
-               wake_q_add(wake_q, tsk);
+               get_task_struct(tsk);
                list_del(&waiter->list);
                /*
-                * Ensure that the last operation is setting the reader
+                * Ensure calling get_task_struct() before setting the reader
                 * waiter to nil such that rwsem_down_read_failed() cannot
                 * race with do_exit() by always holding a reference count
                 * to the task to wakeup.
                 */
                smp_store_release(&waiter->task, NULL);
+               /*
+                * Ensure issuing the wakeup (either by us or someone else)
+                * after setting the reader waiter to nil.
+                */
+               wake_q_add(wake_q, tsk);
+               /* wake_q_add() already take the task ref */
+               put_task_struct(tsk);
        }
 
        adjustment = woken * RWSEM_ACTIVE_READ_BIAS - adjustment;
index 04f2486..9e0f523 100644 (file)
@@ -428,6 +428,8 @@ static struct dentry *relay_create_buf_file(struct rchan *chan,
        dentry = chan->cb->create_buf_file(tmpname, chan->parent,
                                           S_IRUSR, buf,
                                           &chan->is_global);
+       if (IS_ERR(dentry))
+               dentry = NULL;
 
        kfree(tmpname);
 
@@ -461,7 +463,7 @@ static struct rchan_buf *relay_open_buf(struct rchan *chan, unsigned int cpu)
                dentry = chan->cb->create_buf_file(NULL, NULL,
                                                   S_IRUSR, buf,
                                                   &chan->is_global);
-               if (WARN_ON(dentry))
+               if (IS_ERR_OR_NULL(dentry))
                        goto free_buf;
        }
 
index a674c7d..d8d76a6 100644 (file)
@@ -396,6 +396,18 @@ static bool set_nr_if_polling(struct task_struct *p)
 #endif
 #endif
 
+/**
+ * wake_q_add() - queue a wakeup for 'later' waking.
+ * @head: the wake_q_head to add @task to
+ * @task: the task to queue for 'later' wakeup
+ *
+ * Queue a task for later wakeup, most likely by the wake_up_q() call in the
+ * same context, _HOWEVER_ this is not guaranteed, the wakeup can come
+ * instantly.
+ *
+ * This function must be used as-if it were wake_up_process(); IOW the task
+ * must be ready to be woken at this location.
+ */
 void wake_q_add(struct wake_q_head *head, struct task_struct *task)
 {
        struct wake_q_node *node = &task->wake_q;
@@ -405,10 +417,11 @@ void wake_q_add(struct wake_q_head *head, struct task_struct *task)
         * its already queued (either by us or someone else) and will get the
         * wakeup due to that.
         *
-        * This cmpxchg() executes a full barrier, which pairs with the full
-        * barrier executed by the wakeup in wake_up_q().
+        * In order to ensure that a pending wakeup will observe our pending
+        * state, even in the failed case, an explicit smp_mb() must be used.
         */
-       if (cmpxchg(&node->next, NULL, WAKE_Q_TAIL))
+       smp_mb__before_atomic();
+       if (cmpxchg_relaxed(&node->next, NULL, WAKE_Q_TAIL))
                return;
 
        get_task_struct(task);
index 50aa2ab..310d063 100644 (file)
@@ -5980,6 +5980,7 @@ static inline int find_idlest_cpu(struct sched_domain *sd, struct task_struct *p
 
 #ifdef CONFIG_SCHED_SMT
 DEFINE_STATIC_KEY_FALSE(sched_smt_present);
+EXPORT_SYMBOL_GPL(sched_smt_present);
 
 static inline void set_idle_cores(int cpu, int val)
 {
index fe24de3..c348478 100644 (file)
  * sampling of the aggregate task states would be.
  */
 
+#include "../workqueue_internal.h"
 #include <linux/sched/loadavg.h>
 #include <linux/seq_file.h>
 #include <linux/proc_fs.h>
@@ -480,9 +481,6 @@ static void psi_group_change(struct psi_group *group, int cpu,
                        groupc->tasks[t]++;
 
        write_seqcount_end(&groupc->seq);
-
-       if (!delayed_work_pending(&group->clock_work))
-               schedule_delayed_work(&group->clock_work, PSI_FREQ);
 }
 
 static struct psi_group *iterate_groups(struct task_struct *task, void **iter)
@@ -513,6 +511,7 @@ void psi_task_change(struct task_struct *task, int clear, int set)
 {
        int cpu = task_cpu(task);
        struct psi_group *group;
+       bool wake_clock = true;
        void *iter = NULL;
 
        if (!task->pid)
@@ -530,8 +529,22 @@ void psi_task_change(struct task_struct *task, int clear, int set)
        task->psi_flags &= ~clear;
        task->psi_flags |= set;
 
-       while ((group = iterate_groups(task, &iter)))
+       /*
+        * Periodic aggregation shuts off if there is a period of no
+        * task changes, so we wake it back up if necessary. However,
+        * don't do this if the task change is the aggregation worker
+        * itself going to sleep, or we'll ping-pong forever.
+        */
+       if (unlikely((clear & TSK_RUNNING) &&
+                    (task->flags & PF_WQ_WORKER) &&
+                    wq_worker_last_func(task) == psi_update_work))
+               wake_clock = false;
+
+       while ((group = iterate_groups(task, &iter))) {
                psi_group_change(group, cpu, clear, set);
+               if (wake_clock && !delayed_work_pending(&group->clock_work))
+                       schedule_delayed_work(&group->clock_work, PSI_FREQ);
+       }
 }
 
 void psi_memstall_tick(struct task_struct *task, int cpu)
index e1d7ad8..57b7771 100644 (file)
@@ -688,6 +688,48 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, kernel_siginfo_t *in
 }
 EXPORT_SYMBOL_GPL(dequeue_signal);
 
+static int dequeue_synchronous_signal(kernel_siginfo_t *info)
+{
+       struct task_struct *tsk = current;
+       struct sigpending *pending = &tsk->pending;
+       struct sigqueue *q, *sync = NULL;
+
+       /*
+        * Might a synchronous signal be in the queue?
+        */
+       if (!((pending->signal.sig[0] & ~tsk->blocked.sig[0]) & SYNCHRONOUS_MASK))
+               return 0;
+
+       /*
+        * Return the first synchronous signal in the queue.
+        */
+       list_for_each_entry(q, &pending->list, list) {
+               /* Synchronous signals have a postive si_code */
+               if ((q->info.si_code > SI_USER) &&
+                   (sigmask(q->info.si_signo) & SYNCHRONOUS_MASK)) {
+                       sync = q;
+                       goto next;
+               }
+       }
+       return 0;
+next:
+       /*
+        * Check if there is another siginfo for the same signal.
+        */
+       list_for_each_entry_continue(q, &pending->list, list) {
+               if (q->info.si_signo == sync->info.si_signo)
+                       goto still_pending;
+       }
+
+       sigdelset(&pending->signal, sync->info.si_signo);
+       recalc_sigpending();
+still_pending:
+       list_del_init(&sync->list);
+       copy_siginfo(info, &sync->info);
+       __sigqueue_free(sync);
+       return info->si_signo;
+}
+
 /*
  * Tell a process that it has a new active signal..
  *
@@ -1057,10 +1099,9 @@ static int __send_signal(int sig, struct kernel_siginfo *info, struct task_struc
 
        result = TRACE_SIGNAL_DELIVERED;
        /*
-        * Skip useless siginfo allocation for SIGKILL SIGSTOP,
-        * and kernel threads.
+        * Skip useless siginfo allocation for SIGKILL and kernel threads.
         */
-       if (sig_kernel_only(sig) || (t->flags & PF_KTHREAD))
+       if ((sig == SIGKILL) || (t->flags & PF_KTHREAD))
                goto out_set;
 
        /*
@@ -2394,6 +2435,14 @@ relock:
                goto relock;
        }
 
+       /* Has this task already been marked for death? */
+       if (signal_group_exit(signal)) {
+               ksig->info.si_signo = signr = SIGKILL;
+               sigdelset(&current->pending.signal, SIGKILL);
+               recalc_sigpending();
+               goto fatal;
+       }
+
        for (;;) {
                struct k_sigaction *ka;
 
@@ -2407,7 +2456,15 @@ relock:
                        goto relock;
                }
 
-               signr = dequeue_signal(current, &current->blocked, &ksig->info);
+               /*
+                * Signals generated by the execution of an instruction
+                * need to be delivered before any other pending signals
+                * so that the instruction pointer in the signal stack
+                * frame points to the faulting instruction.
+                */
+               signr = dequeue_synchronous_signal(&ksig->info);
+               if (!signr)
+                       signr = dequeue_signal(current, &current->blocked, &ksig->info);
 
                if (!signr)
                        break; /* will return 0 */
@@ -2489,6 +2546,7 @@ relock:
                        continue;
                }
 
+       fatal:
                spin_unlock_irq(&sighand->siglock);
 
                /*
index 163c451..f4cf1b0 100644 (file)
@@ -584,8 +584,6 @@ void __init smp_init(void)
                num_nodes, (num_nodes > 1 ? "s" : ""),
                num_cpus,  (num_cpus  > 1 ? "s" : ""));
 
-       /* Final decision about SMT support */
-       cpu_smt_check_topology();
        /* Any cleanup work */
        smp_cpus_done(setup_max_cpus);
 }
index 8f0644a..80f9552 100644 (file)
@@ -685,6 +685,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
         * set up the signal and overrun bookkeeping.
         */
        timer->it.cpu.incr = timespec64_to_ns(&new->it_interval);
+       timer->it_interval = ns_to_ktime(timer->it.cpu.incr);
 
        /*
         * This acts as a modification timestamp for the timer,
index 8b068ad..f1a86a0 100644 (file)
@@ -1204,22 +1204,12 @@ static int __bpf_probe_register(struct bpf_raw_event_map *btp, struct bpf_prog *
 
 int bpf_probe_register(struct bpf_raw_event_map *btp, struct bpf_prog *prog)
 {
-       int err;
-
-       mutex_lock(&bpf_event_mutex);
-       err = __bpf_probe_register(btp, prog);
-       mutex_unlock(&bpf_event_mutex);
-       return err;
+       return __bpf_probe_register(btp, prog);
 }
 
 int bpf_probe_unregister(struct bpf_raw_event_map *btp, struct bpf_prog *prog)
 {
-       int err;
-
-       mutex_lock(&bpf_event_mutex);
-       err = tracepoint_probe_unregister(btp->tp, (void *)btp->bpf_func, prog);
-       mutex_unlock(&bpf_event_mutex);
-       return err;
+       return tracepoint_probe_unregister(btp->tp, (void *)btp->bpf_func, prog);
 }
 
 int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
index 5c56afc..4737bb8 100644 (file)
@@ -180,10 +180,12 @@ store_trace_args(void *data, struct trace_probe *tp, struct pt_regs *regs,
                if (unlikely(arg->dynamic))
                        *dl = make_data_loc(maxlen, dyndata - base);
                ret = process_fetch_insn(arg->code, regs, dl, base);
-               if (unlikely(ret < 0 && arg->dynamic))
+               if (unlikely(ret < 0 && arg->dynamic)) {
                        *dl = make_data_loc(0, dyndata - base);
-               else
+               } else {
                        dyndata += ret;
+                       maxlen -= ret;
+               }
        }
 }
 
index e335576..9bde07c 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) IBM Corporation, 2010-2012
  * Author:     Srikar Dronamraju <srikar@linux.vnet.ibm.com>
  */
-#define pr_fmt(fmt)    "trace_kprobe: " fmt
+#define pr_fmt(fmt)    "trace_uprobe: " fmt
 
 #include <linux/ctype.h>
 #include <linux/module.h>
@@ -160,6 +160,13 @@ fetch_store_string(unsigned long addr, void *dest, void *base)
        if (ret >= 0) {
                if (ret == maxlen)
                        dst[ret - 1] = '\0';
+               else
+                       /*
+                        * Include the terminating null byte. In this case it
+                        * was copied by strncpy_from_user but not accounted
+                        * for in ret.
+                        */
+                       ret++;
                *(u32 *)dest = make_data_loc(ret, (void *)dst - base);
        }
 
index 392be4b..fc5d23d 100644 (file)
@@ -910,6 +910,26 @@ struct task_struct *wq_worker_sleeping(struct task_struct *task)
 }
 
 /**
+ * wq_worker_last_func - retrieve worker's last work function
+ *
+ * Determine the last function a worker executed. This is called from
+ * the scheduler to get a worker's last known identity.
+ *
+ * CONTEXT:
+ * spin_lock_irq(rq->lock)
+ *
+ * Return:
+ * The last work function %current executed as a worker, NULL if it
+ * hasn't executed any work yet.
+ */
+work_func_t wq_worker_last_func(struct task_struct *task)
+{
+       struct worker *worker = kthread_data(task);
+
+       return worker->last_func;
+}
+
+/**
  * worker_set_flags - set worker flags and adjust nr_running accordingly
  * @worker: self
  * @flags: flags to set
@@ -2184,6 +2204,9 @@ __acquires(&pool->lock)
        if (unlikely(cpu_intensive))
                worker_clr_flags(worker, WORKER_CPU_INTENSIVE);
 
+       /* tag the worker for identification in schedule() */
+       worker->last_func = worker->current_func;
+
        /* we're done with it, release */
        hash_del(&worker->hentry);
        worker->current_work = NULL;
index 66fbb5a..cb68b03 100644 (file)
@@ -53,6 +53,9 @@ struct worker {
 
        /* used only by rescuers to point to the target workqueue */
        struct workqueue_struct *rescue_wq;     /* I: the workqueue to rescue */
+
+       /* used by the scheduler to determine a worker's last known identity */
+       work_func_t             last_func;
 };
 
 /**
@@ -67,9 +70,10 @@ static inline struct worker *current_wq_worker(void)
 
 /*
  * Scheduler hooks for concurrency managed workqueue.  Only to be used from
- * sched/core.c and workqueue.c.
+ * sched/ and workqueue.c.
  */
 void wq_worker_waking_up(struct task_struct *task, int cpu);
 struct task_struct *wq_worker_sleeping(struct task_struct *task);
+work_func_t wq_worker_last_func(struct task_struct *task);
 
 #endif /* _KERNEL_WORKQUEUE_INTERNAL_H */
index c9b457a..576be22 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/rhashtable.h>
+#include <linux/idr.h>
 #include <linux/list.h>
 #include <linux/sort.h>
 #include <linux/objagg.h>
 #define CREATE_TRACE_POINTS
 #include <trace/events/objagg.h>
 
+struct objagg_hints {
+       struct rhashtable node_ht;
+       struct rhashtable_params ht_params;
+       struct list_head node_list;
+       unsigned int node_count;
+       unsigned int root_count;
+       unsigned int refcount;
+       const struct objagg_ops *ops;
+};
+
+struct objagg_hints_node {
+       struct rhash_head ht_node; /* member of objagg_hints->node_ht */
+       struct list_head list; /* member of objagg_hints->node_list */
+       struct objagg_hints_node *parent;
+       unsigned int root_id;
+       struct objagg_obj_stats_info stats_info;
+       unsigned long obj[0];
+};
+
+static struct objagg_hints_node *
+objagg_hints_lookup(struct objagg_hints *objagg_hints, void *obj)
+{
+       if (!objagg_hints)
+               return NULL;
+       return rhashtable_lookup_fast(&objagg_hints->node_ht, obj,
+                                     objagg_hints->ht_params);
+}
+
 struct objagg {
        const struct objagg_ops *ops;
        void *priv;
@@ -18,6 +47,8 @@ struct objagg {
        struct rhashtable_params ht_params;
        struct list_head obj_list;
        unsigned int obj_count;
+       struct ida root_ida;
+       struct objagg_hints *hints;
 };
 
 struct objagg_obj {
@@ -30,6 +61,7 @@ struct objagg_obj {
                void *delta_priv; /* user delta private */
                void *root_priv; /* user root private */
        };
+       unsigned int root_id;
        unsigned int refcount; /* counts number of users of this object
                                * including nested objects
                                */
@@ -130,7 +162,8 @@ static struct objagg_obj *objagg_obj_lookup(struct objagg *objagg, void *obj)
 
 static int objagg_obj_parent_assign(struct objagg *objagg,
                                    struct objagg_obj *objagg_obj,
-                                   struct objagg_obj *parent)
+                                   struct objagg_obj *parent,
+                                   bool take_parent_ref)
 {
        void *delta_priv;
 
@@ -144,7 +177,8 @@ static int objagg_obj_parent_assign(struct objagg *objagg,
         */
        objagg_obj->parent = parent;
        objagg_obj->delta_priv = delta_priv;
-       objagg_obj_ref_inc(objagg_obj->parent);
+       if (take_parent_ref)
+               objagg_obj_ref_inc(objagg_obj->parent);
        trace_objagg_obj_parent_assign(objagg, objagg_obj,
                                       parent,
                                       parent->refcount);
@@ -164,7 +198,7 @@ static int objagg_obj_parent_lookup_assign(struct objagg *objagg,
                if (!objagg_obj_is_root(objagg_obj_cur))
                        continue;
                err = objagg_obj_parent_assign(objagg, objagg_obj,
-                                              objagg_obj_cur);
+                                              objagg_obj_cur, true);
                if (!err)
                        return 0;
        }
@@ -184,16 +218,68 @@ static void objagg_obj_parent_unassign(struct objagg *objagg,
        __objagg_obj_put(objagg, objagg_obj->parent);
 }
 
+static int objagg_obj_root_id_alloc(struct objagg *objagg,
+                                   struct objagg_obj *objagg_obj,
+                                   struct objagg_hints_node *hnode)
+{
+       unsigned int min, max;
+       int root_id;
+
+       /* In case there are no hints available, the root id is invalid. */
+       if (!objagg->hints) {
+               objagg_obj->root_id = OBJAGG_OBJ_ROOT_ID_INVALID;
+               return 0;
+       }
+
+       if (hnode) {
+               min = hnode->root_id;
+               max = hnode->root_id;
+       } else {
+               /* For objects with no hint, start after the last
+                * hinted root_id.
+                */
+               min = objagg->hints->root_count;
+               max = ~0;
+       }
+
+       root_id = ida_alloc_range(&objagg->root_ida, min, max, GFP_KERNEL);
+
+       if (root_id < 0)
+               return root_id;
+       objagg_obj->root_id = root_id;
+       return 0;
+}
+
+static void objagg_obj_root_id_free(struct objagg *objagg,
+                                   struct objagg_obj *objagg_obj)
+{
+       if (!objagg->hints)
+               return;
+       ida_free(&objagg->root_ida, objagg_obj->root_id);
+}
+
 static int objagg_obj_root_create(struct objagg *objagg,
-                                 struct objagg_obj *objagg_obj)
+                                 struct objagg_obj *objagg_obj,
+                                 struct objagg_hints_node *hnode)
 {
-       objagg_obj->root_priv = objagg->ops->root_create(objagg->priv,
-                                                        objagg_obj->obj);
-       if (IS_ERR(objagg_obj->root_priv))
-               return PTR_ERR(objagg_obj->root_priv);
+       int err;
 
+       err = objagg_obj_root_id_alloc(objagg, objagg_obj, hnode);
+       if (err)
+               return err;
+       objagg_obj->root_priv = objagg->ops->root_create(objagg->priv,
+                                                        objagg_obj->obj,
+                                                        objagg_obj->root_id);
+       if (IS_ERR(objagg_obj->root_priv)) {
+               err = PTR_ERR(objagg_obj->root_priv);
+               goto err_root_create;
+       }
        trace_objagg_obj_root_create(objagg, objagg_obj);
        return 0;
+
+err_root_create:
+       objagg_obj_root_id_free(objagg, objagg_obj);
+       return err;
 }
 
 static void objagg_obj_root_destroy(struct objagg *objagg,
@@ -201,19 +287,69 @@ static void objagg_obj_root_destroy(struct objagg *objagg,
 {
        trace_objagg_obj_root_destroy(objagg, objagg_obj);
        objagg->ops->root_destroy(objagg->priv, objagg_obj->root_priv);
+       objagg_obj_root_id_free(objagg, objagg_obj);
+}
+
+static struct objagg_obj *__objagg_obj_get(struct objagg *objagg, void *obj);
+
+static int objagg_obj_init_with_hints(struct objagg *objagg,
+                                     struct objagg_obj *objagg_obj,
+                                     bool *hint_found)
+{
+       struct objagg_hints_node *hnode;
+       struct objagg_obj *parent;
+       int err;
+
+       hnode = objagg_hints_lookup(objagg->hints, objagg_obj->obj);
+       if (!hnode) {
+               *hint_found = false;
+               return 0;
+       }
+       *hint_found = true;
+
+       if (!hnode->parent)
+               return objagg_obj_root_create(objagg, objagg_obj, hnode);
+
+       parent = __objagg_obj_get(objagg, hnode->parent->obj);
+       if (IS_ERR(parent))
+               return PTR_ERR(parent);
+
+       err = objagg_obj_parent_assign(objagg, objagg_obj, parent, false);
+       if (err) {
+               *hint_found = false;
+               err = 0;
+               goto err_parent_assign;
+       }
+
+       return 0;
+
+err_parent_assign:
+       objagg_obj_put(objagg, parent);
+       return err;
 }
 
 static int objagg_obj_init(struct objagg *objagg,
                           struct objagg_obj *objagg_obj)
 {
+       bool hint_found;
        int err;
 
+       /* First, try to use hints if they are available and
+        * if they provide result.
+        */
+       err = objagg_obj_init_with_hints(objagg, objagg_obj, &hint_found);
+       if (err)
+               return err;
+
+       if (hint_found)
+               return 0;
+
        /* Try to find if the object can be aggregated under an existing one. */
        err = objagg_obj_parent_lookup_assign(objagg, objagg_obj);
        if (!err)
                return 0;
        /* If aggregation is not possible, make the object a root. */
-       return objagg_obj_root_create(objagg, objagg_obj);
+       return objagg_obj_root_create(objagg, objagg_obj, NULL);
 }
 
 static void objagg_obj_fini(struct objagg *objagg,
@@ -349,8 +485,9 @@ EXPORT_SYMBOL(objagg_obj_put);
 
 /**
  * objagg_create - creates a new objagg instance
- * @ops:       user-specific callbacks
- * @priv:      pointer to a private data passed to the ops
+ * @ops:               user-specific callbacks
+ * @objagg_hints:      hints, can be NULL
+ * @priv:              pointer to a private data passed to the ops
  *
  * Note: all locking must be provided by the caller.
  *
@@ -374,18 +511,25 @@ EXPORT_SYMBOL(objagg_obj_put);
  * Returns a pointer to newly created objagg instance in case of success,
  * otherwise it returns pointer error using ERR_PTR macro.
  */
-struct objagg *objagg_create(const struct objagg_ops *ops, void *priv)
+struct objagg *objagg_create(const struct objagg_ops *ops,
+                            struct objagg_hints *objagg_hints, void *priv)
 {
        struct objagg *objagg;
        int err;
 
        if (WARN_ON(!ops || !ops->root_create || !ops->root_destroy ||
-                   !ops->delta_create || !ops->delta_destroy))
+                   !ops->delta_check || !ops->delta_create ||
+                   !ops->delta_destroy))
                return ERR_PTR(-EINVAL);
+
        objagg = kzalloc(sizeof(*objagg), GFP_KERNEL);
        if (!objagg)
                return ERR_PTR(-ENOMEM);
        objagg->ops = ops;
+       if (objagg_hints) {
+               objagg->hints = objagg_hints;
+               objagg_hints->refcount++;
+       }
        objagg->priv = priv;
        INIT_LIST_HEAD(&objagg->obj_list);
 
@@ -397,6 +541,8 @@ struct objagg *objagg_create(const struct objagg_ops *ops, void *priv)
        if (err)
                goto err_rhashtable_init;
 
+       ida_init(&objagg->root_ida);
+
        trace_objagg_create(objagg);
        return objagg;
 
@@ -415,8 +561,11 @@ EXPORT_SYMBOL(objagg_create);
 void objagg_destroy(struct objagg *objagg)
 {
        trace_objagg_destroy(objagg);
+       ida_destroy(&objagg->root_ida);
        WARN_ON(!list_empty(&objagg->obj_list));
        rhashtable_destroy(&objagg->obj_ht);
+       if (objagg->hints)
+               objagg_hints_put(objagg->hints);
        kfree(objagg);
 }
 EXPORT_SYMBOL(objagg_destroy);
@@ -472,6 +621,8 @@ const struct objagg_stats *objagg_stats_get(struct objagg *objagg)
                objagg_stats->stats_info[i].objagg_obj = objagg_obj;
                objagg_stats->stats_info[i].is_root =
                                        objagg_obj_is_root(objagg_obj);
+               if (objagg_stats->stats_info[i].is_root)
+                       objagg_stats->root_count++;
                i++;
        }
        objagg_stats->stats_info_count = i;
@@ -485,7 +636,7 @@ const struct objagg_stats *objagg_stats_get(struct objagg *objagg)
 EXPORT_SYMBOL(objagg_stats_get);
 
 /**
- * objagg_stats_puts - puts stats of the objagg instance
+ * objagg_stats_put - puts stats of the objagg instance
  * @objagg_stats:      objagg instance stats
  *
  * Note: all locking must be provided by the caller.
@@ -496,6 +647,410 @@ void objagg_stats_put(const struct objagg_stats *objagg_stats)
 }
 EXPORT_SYMBOL(objagg_stats_put);
 
+static struct objagg_hints_node *
+objagg_hints_node_create(struct objagg_hints *objagg_hints,
+                        struct objagg_obj *objagg_obj, size_t obj_size,
+                        struct objagg_hints_node *parent_hnode)
+{
+       unsigned int user_count = objagg_obj->stats.user_count;
+       struct objagg_hints_node *hnode;
+       int err;
+
+       hnode = kzalloc(sizeof(*hnode) + obj_size, GFP_KERNEL);
+       if (!hnode)
+               return ERR_PTR(-ENOMEM);
+       memcpy(hnode->obj, &objagg_obj->obj, obj_size);
+       hnode->stats_info.stats.user_count = user_count;
+       hnode->stats_info.stats.delta_user_count = user_count;
+       if (parent_hnode) {
+               parent_hnode->stats_info.stats.delta_user_count += user_count;
+       } else {
+               hnode->root_id = objagg_hints->root_count++;
+               hnode->stats_info.is_root = true;
+       }
+       hnode->stats_info.objagg_obj = objagg_obj;
+
+       err = rhashtable_insert_fast(&objagg_hints->node_ht, &hnode->ht_node,
+                                    objagg_hints->ht_params);
+       if (err)
+               goto err_ht_insert;
+
+       list_add(&hnode->list, &objagg_hints->node_list);
+       hnode->parent = parent_hnode;
+       objagg_hints->node_count++;
+
+       return hnode;
+
+err_ht_insert:
+       kfree(hnode);
+       return ERR_PTR(err);
+}
+
+static void objagg_hints_flush(struct objagg_hints *objagg_hints)
+{
+       struct objagg_hints_node *hnode, *tmp;
+
+       list_for_each_entry_safe(hnode, tmp, &objagg_hints->node_list, list) {
+               list_del(&hnode->list);
+               rhashtable_remove_fast(&objagg_hints->node_ht, &hnode->ht_node,
+                                      objagg_hints->ht_params);
+               kfree(hnode);
+       }
+}
+
+struct objagg_tmp_node {
+       struct objagg_obj *objagg_obj;
+       bool crossed_out;
+};
+
+struct objagg_tmp_graph {
+       struct objagg_tmp_node *nodes;
+       unsigned long nodes_count;
+       unsigned long *edges;
+};
+
+static int objagg_tmp_graph_edge_index(struct objagg_tmp_graph *graph,
+                                      int parent_index, int index)
+{
+       return index * graph->nodes_count + parent_index;
+}
+
+static void objagg_tmp_graph_edge_set(struct objagg_tmp_graph *graph,
+                                     int parent_index, int index)
+{
+       int edge_index = objagg_tmp_graph_edge_index(graph, index,
+                                                    parent_index);
+
+       __set_bit(edge_index, graph->edges);
+}
+
+static bool objagg_tmp_graph_is_edge(struct objagg_tmp_graph *graph,
+                                    int parent_index, int index)
+{
+       int edge_index = objagg_tmp_graph_edge_index(graph, index,
+                                                    parent_index);
+
+       return test_bit(edge_index, graph->edges);
+}
+
+static unsigned int objagg_tmp_graph_node_weight(struct objagg_tmp_graph *graph,
+                                                unsigned int index)
+{
+       struct objagg_tmp_node *node = &graph->nodes[index];
+       unsigned int weight = node->objagg_obj->stats.user_count;
+       int j;
+
+       /* Node weight is sum of node users and all other nodes users
+        * that this node can represent with delta.
+        */
+
+       for (j = 0; j < graph->nodes_count; j++) {
+               if (!objagg_tmp_graph_is_edge(graph, index, j))
+                       continue;
+               node = &graph->nodes[j];
+               if (node->crossed_out)
+                       continue;
+               weight += node->objagg_obj->stats.user_count;
+       }
+       return weight;
+}
+
+static int objagg_tmp_graph_node_max_weight(struct objagg_tmp_graph *graph)
+{
+       struct objagg_tmp_node *node;
+       unsigned int max_weight = 0;
+       unsigned int weight;
+       int max_index = -1;
+       int i;
+
+       for (i = 0; i < graph->nodes_count; i++) {
+               node = &graph->nodes[i];
+               if (node->crossed_out)
+                       continue;
+               weight = objagg_tmp_graph_node_weight(graph, i);
+               if (weight >= max_weight) {
+                       max_weight = weight;
+                       max_index = i;
+               }
+       }
+       return max_index;
+}
+
+static struct objagg_tmp_graph *objagg_tmp_graph_create(struct objagg *objagg)
+{
+       unsigned int nodes_count = objagg->obj_count;
+       struct objagg_tmp_graph *graph;
+       struct objagg_tmp_node *node;
+       struct objagg_tmp_node *pnode;
+       struct objagg_obj *objagg_obj;
+       size_t alloc_size;
+       int i, j;
+
+       graph = kzalloc(sizeof(*graph), GFP_KERNEL);
+       if (!graph)
+               return NULL;
+
+       graph->nodes = kcalloc(nodes_count, sizeof(*graph->nodes), GFP_KERNEL);
+       if (!graph->nodes)
+               goto err_nodes_alloc;
+       graph->nodes_count = nodes_count;
+
+       alloc_size = BITS_TO_LONGS(nodes_count * nodes_count) *
+                    sizeof(unsigned long);
+       graph->edges = kzalloc(alloc_size, GFP_KERNEL);
+       if (!graph->edges)
+               goto err_edges_alloc;
+
+       i = 0;
+       list_for_each_entry(objagg_obj, &objagg->obj_list, list) {
+               node = &graph->nodes[i++];
+               node->objagg_obj = objagg_obj;
+       }
+
+       /* Assemble a temporary graph. Insert edge X->Y in case Y can be
+        * in delta of X.
+        */
+       for (i = 0; i < nodes_count; i++) {
+               for (j = 0; j < nodes_count; j++) {
+                       if (i == j)
+                               continue;
+                       pnode = &graph->nodes[i];
+                       node = &graph->nodes[j];
+                       if (objagg->ops->delta_check(objagg->priv,
+                                                    pnode->objagg_obj->obj,
+                                                    node->objagg_obj->obj)) {
+                               objagg_tmp_graph_edge_set(graph, i, j);
+
+                       }
+               }
+       }
+       return graph;
+
+err_edges_alloc:
+       kfree(graph->nodes);
+err_nodes_alloc:
+       kfree(graph);
+       return NULL;
+}
+
+static void objagg_tmp_graph_destroy(struct objagg_tmp_graph *graph)
+{
+       kfree(graph->edges);
+       kfree(graph->nodes);
+       kfree(graph);
+}
+
+static int
+objagg_opt_simple_greedy_fillup_hints(struct objagg_hints *objagg_hints,
+                                     struct objagg *objagg)
+{
+       struct objagg_hints_node *hnode, *parent_hnode;
+       struct objagg_tmp_graph *graph;
+       struct objagg_tmp_node *node;
+       int index;
+       int j;
+       int err;
+
+       graph = objagg_tmp_graph_create(objagg);
+       if (!graph)
+               return -ENOMEM;
+
+       /* Find the nodes from the ones that can accommodate most users
+        * and cross them out of the graph. Save them to the hint list.
+        */
+       while ((index = objagg_tmp_graph_node_max_weight(graph)) != -1) {
+               node = &graph->nodes[index];
+               node->crossed_out = true;
+               hnode = objagg_hints_node_create(objagg_hints,
+                                                node->objagg_obj,
+                                                objagg->ops->obj_size,
+                                                NULL);
+               if (IS_ERR(hnode)) {
+                       err = PTR_ERR(hnode);
+                       goto out;
+               }
+               parent_hnode = hnode;
+               for (j = 0; j < graph->nodes_count; j++) {
+                       if (!objagg_tmp_graph_is_edge(graph, index, j))
+                               continue;
+                       node = &graph->nodes[j];
+                       if (node->crossed_out)
+                               continue;
+                       node->crossed_out = true;
+                       hnode = objagg_hints_node_create(objagg_hints,
+                                                        node->objagg_obj,
+                                                        objagg->ops->obj_size,
+                                                        parent_hnode);
+                       if (IS_ERR(hnode)) {
+                               err = PTR_ERR(hnode);
+                               goto out;
+                       }
+               }
+       }
+
+       err = 0;
+out:
+       objagg_tmp_graph_destroy(graph);
+       return err;
+}
+
+struct objagg_opt_algo {
+       int (*fillup_hints)(struct objagg_hints *objagg_hints,
+                           struct objagg *objagg);
+};
+
+static const struct objagg_opt_algo objagg_opt_simple_greedy = {
+       .fillup_hints = objagg_opt_simple_greedy_fillup_hints,
+};
+
+
+static const struct objagg_opt_algo *objagg_opt_algos[] = {
+       [OBJAGG_OPT_ALGO_SIMPLE_GREEDY] = &objagg_opt_simple_greedy,
+};
+
+static int objagg_hints_obj_cmp(struct rhashtable_compare_arg *arg,
+                               const void *obj)
+{
+       struct rhashtable *ht = arg->ht;
+       struct objagg_hints *objagg_hints =
+                       container_of(ht, struct objagg_hints, node_ht);
+       const struct objagg_ops *ops = objagg_hints->ops;
+       const char *ptr = obj;
+
+       ptr += ht->p.key_offset;
+       return ops->hints_obj_cmp ? ops->hints_obj_cmp(ptr, arg->key) :
+                                   memcmp(ptr, arg->key, ht->p.key_len);
+}
+
+/**
+ * objagg_hints_get - obtains hints instance
+ * @objagg:            objagg instance
+ * @opt_algo_type:     type of hints finding algorithm
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * According to the algo type, the existing objects of objagg instance
+ * are going to be went-through to assemble an optimal tree. We call this
+ * tree hints. These hints can be later on used for creation of
+ * a new objagg instance. There, the future object creations are going
+ * to be consulted with these hints in order to find out, where exactly
+ * the new object should be put as a root or delta.
+ *
+ * Returns a pointer to hints instance in case of success,
+ * otherwise it returns pointer error using ERR_PTR macro.
+ */
+struct objagg_hints *objagg_hints_get(struct objagg *objagg,
+                                     enum objagg_opt_algo_type opt_algo_type)
+{
+       const struct objagg_opt_algo *algo = objagg_opt_algos[opt_algo_type];
+       struct objagg_hints *objagg_hints;
+       int err;
+
+       objagg_hints = kzalloc(sizeof(*objagg_hints), GFP_KERNEL);
+       if (!objagg_hints)
+               return ERR_PTR(-ENOMEM);
+
+       objagg_hints->ops = objagg->ops;
+       objagg_hints->refcount = 1;
+
+       INIT_LIST_HEAD(&objagg_hints->node_list);
+
+       objagg_hints->ht_params.key_len = objagg->ops->obj_size;
+       objagg_hints->ht_params.key_offset =
+                               offsetof(struct objagg_hints_node, obj);
+       objagg_hints->ht_params.head_offset =
+                               offsetof(struct objagg_hints_node, ht_node);
+       objagg_hints->ht_params.obj_cmpfn = objagg_hints_obj_cmp;
+
+       err = rhashtable_init(&objagg_hints->node_ht, &objagg_hints->ht_params);
+       if (err)
+               goto err_rhashtable_init;
+
+       err = algo->fillup_hints(objagg_hints, objagg);
+       if (err)
+               goto err_fillup_hints;
+
+       if (WARN_ON(objagg_hints->node_count != objagg->obj_count)) {
+               err = -EINVAL;
+               goto err_node_count_check;
+       }
+
+       return objagg_hints;
+
+err_node_count_check:
+err_fillup_hints:
+       objagg_hints_flush(objagg_hints);
+       rhashtable_destroy(&objagg_hints->node_ht);
+err_rhashtable_init:
+       kfree(objagg_hints);
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL(objagg_hints_get);
+
+/**
+ * objagg_hints_put - puts hints instance
+ * @objagg_hints:      objagg hints instance
+ *
+ * Note: all locking must be provided by the caller.
+ */
+void objagg_hints_put(struct objagg_hints *objagg_hints)
+{
+       if (--objagg_hints->refcount)
+               return;
+       objagg_hints_flush(objagg_hints);
+       rhashtable_destroy(&objagg_hints->node_ht);
+       kfree(objagg_hints);
+}
+EXPORT_SYMBOL(objagg_hints_put);
+
+/**
+ * objagg_hints_stats_get - obtains stats of the hints instance
+ * @objagg_hints:      hints instance
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * The returned structure contains statistics of all objects
+ * currently in use, ordered by following rules:
+ * 1) Root objects are always on lower indexes than the rest.
+ * 2) Objects with higher delta user count are always on lower
+ *    indexes.
+ * 3) In case multiple objects have the same delta user count,
+ *    the objects are ordered by user count.
+ *
+ * Returns a pointer to stats instance in case of success,
+ * otherwise it returns pointer error using ERR_PTR macro.
+ */
+const struct objagg_stats *
+objagg_hints_stats_get(struct objagg_hints *objagg_hints)
+{
+       struct objagg_stats *objagg_stats;
+       struct objagg_hints_node *hnode;
+       int i;
+
+       objagg_stats = kzalloc(struct_size(objagg_stats, stats_info,
+                                          objagg_hints->node_count),
+                              GFP_KERNEL);
+       if (!objagg_stats)
+               return ERR_PTR(-ENOMEM);
+
+       i = 0;
+       list_for_each_entry(hnode, &objagg_hints->node_list, list) {
+               memcpy(&objagg_stats->stats_info[i], &hnode->stats_info,
+                      sizeof(objagg_stats->stats_info[0]));
+               if (objagg_stats->stats_info[i].is_root)
+                       objagg_stats->root_count++;
+               i++;
+       }
+       objagg_stats->stats_info_count = i;
+
+       sort(objagg_stats->stats_info, objagg_stats->stats_info_count,
+            sizeof(struct objagg_obj_stats_info),
+            objagg_stats_info_sort_cmp_func, NULL);
+
+       return objagg_stats;
+}
+EXPORT_SYMBOL(objagg_hints_stats_get);
+
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
 MODULE_DESCRIPTION("Object aggregation manager");
index d82d022..9cf7762 100644 (file)
@@ -632,7 +632,7 @@ static void __kmod_config_free(struct test_config *config)
        config->test_driver = NULL;
 
        kfree_const(config->test_fs);
-       config->test_driver = NULL;
+       config->test_fs = NULL;
 }
 
 static void kmod_config_free(struct kmod_test_device *test_dev)
index ab57144..72c1abf 100644 (file)
@@ -87,6 +87,15 @@ static void world_obj_put(struct world *world, struct objagg *objagg,
 
 #define MAX_KEY_ID_DIFF 5
 
+static bool delta_check(void *priv, const void *parent_obj, const void *obj)
+{
+       const struct tokey *parent_key = parent_obj;
+       const struct tokey *key = obj;
+       int diff = key->id - parent_key->id;
+
+       return diff >= 0 && diff <= MAX_KEY_ID_DIFF;
+}
+
 static void *delta_create(void *priv, void *parent_obj, void *obj)
 {
        struct tokey *parent_key = parent_obj;
@@ -95,7 +104,7 @@ static void *delta_create(void *priv, void *parent_obj, void *obj)
        int diff = key->id - parent_key->id;
        struct delta *delta;
 
-       if (diff < 0 || diff > MAX_KEY_ID_DIFF)
+       if (!delta_check(priv, parent_obj, obj))
                return ERR_PTR(-EINVAL);
 
        delta = kzalloc(sizeof(*delta), GFP_KERNEL);
@@ -115,7 +124,7 @@ static void delta_destroy(void *priv, void *delta_priv)
        kfree(delta);
 }
 
-static void *root_create(void *priv, void *obj)
+static void *root_create(void *priv, void *obj, unsigned int id)
 {
        struct world *world = priv;
        struct tokey *key = obj;
@@ -268,6 +277,12 @@ stats_put:
        return err;
 }
 
+static bool delta_check_dummy(void *priv, const void *parent_obj,
+                             const void *obj)
+{
+       return false;
+}
+
 static void *delta_create_dummy(void *priv, void *parent_obj, void *obj)
 {
        return ERR_PTR(-EOPNOTSUPP);
@@ -279,6 +294,7 @@ static void delta_destroy_dummy(void *priv, void *delta_priv)
 
 static const struct objagg_ops nodelta_ops = {
        .obj_size = sizeof(struct tokey),
+       .delta_check = delta_check_dummy,
        .delta_create = delta_create_dummy,
        .delta_destroy = delta_destroy_dummy,
        .root_create = root_create,
@@ -292,7 +308,7 @@ static int test_nodelta(void)
        int i;
        int err;
 
-       objagg = objagg_create(&nodelta_ops, &world);
+       objagg = objagg_create(&nodelta_ops, NULL, &world);
        if (IS_ERR(objagg))
                return PTR_ERR(objagg);
 
@@ -357,6 +373,7 @@ err_stats_second_zero:
 
 static const struct objagg_ops delta_ops = {
        .obj_size = sizeof(struct tokey),
+       .delta_check = delta_check,
        .delta_create = delta_create,
        .delta_destroy = delta_destroy,
        .root_create = root_create,
@@ -728,8 +745,10 @@ static int check_expect_stats(struct objagg *objagg,
        int err;
 
        stats = objagg_stats_get(objagg);
-       if (IS_ERR(stats))
+       if (IS_ERR(stats)) {
+               *errmsg = "objagg_stats_get() failed.";
                return PTR_ERR(stats);
+       }
        err = __check_expect_stats(stats, expect_stats, errmsg);
        objagg_stats_put(stats);
        return err;
@@ -769,7 +788,6 @@ static int test_delta_action_item(struct world *world,
        if (err)
                goto errout;
 
-       errmsg = NULL;
        err = check_expect_stats(objagg, &action_item->expect_stats, &errmsg);
        if (err) {
                pr_err("Key %u: Stats: %s\n", action_item->key_id, errmsg);
@@ -793,7 +811,7 @@ static int test_delta(void)
        int i;
        int err;
 
-       objagg = objagg_create(&delta_ops, &world);
+       objagg = objagg_create(&delta_ops, NULL, &world);
        if (IS_ERR(objagg))
                return PTR_ERR(objagg);
 
@@ -815,6 +833,170 @@ err_do_action_item:
        return err;
 }
 
+struct hints_case {
+       const unsigned int *key_ids;
+       size_t key_ids_count;
+       struct expect_stats expect_stats;
+       struct expect_stats expect_stats_hints;
+};
+
+static const unsigned int hints_case_key_ids[] = {
+       1, 7, 3, 5, 3, 1, 30, 8, 8, 5, 6, 8,
+};
+
+static const struct hints_case hints_case = {
+       .key_ids = hints_case_key_ids,
+       .key_ids_count = ARRAY_SIZE(hints_case_key_ids),
+       .expect_stats =
+               EXPECT_STATS(7, ROOT(1, 2, 7), ROOT(7, 1, 4), ROOT(30, 1, 1),
+                               DELTA(8, 3), DELTA(3, 2),
+                               DELTA(5, 2), DELTA(6, 1)),
+       .expect_stats_hints =
+               EXPECT_STATS(7, ROOT(3, 2, 9), ROOT(1, 2, 2), ROOT(30, 1, 1),
+                               DELTA(8, 3), DELTA(5, 2),
+                               DELTA(6, 1), DELTA(7, 1)),
+};
+
+static void __pr_debug_stats(const struct objagg_stats *stats)
+{
+       int i;
+
+       for (i = 0; i < stats->stats_info_count; i++)
+               pr_debug("Stat index %d key %u: u %d, d %d, %s\n", i,
+                        obj_to_key_id(stats->stats_info[i].objagg_obj),
+                        stats->stats_info[i].stats.user_count,
+                        stats->stats_info[i].stats.delta_user_count,
+                        stats->stats_info[i].is_root ? "root" : "noroot");
+}
+
+static void pr_debug_stats(struct objagg *objagg)
+{
+       const struct objagg_stats *stats;
+
+       stats = objagg_stats_get(objagg);
+       if (IS_ERR(stats))
+               return;
+       __pr_debug_stats(stats);
+       objagg_stats_put(stats);
+}
+
+static void pr_debug_hints_stats(struct objagg_hints *objagg_hints)
+{
+       const struct objagg_stats *stats;
+
+       stats = objagg_hints_stats_get(objagg_hints);
+       if (IS_ERR(stats))
+               return;
+       __pr_debug_stats(stats);
+       objagg_stats_put(stats);
+}
+
+static int check_expect_hints_stats(struct objagg_hints *objagg_hints,
+                                   const struct expect_stats *expect_stats,
+                                   const char **errmsg)
+{
+       const struct objagg_stats *stats;
+       int err;
+
+       stats = objagg_hints_stats_get(objagg_hints);
+       if (IS_ERR(stats))
+               return PTR_ERR(stats);
+       err = __check_expect_stats(stats, expect_stats, errmsg);
+       objagg_stats_put(stats);
+       return err;
+}
+
+static int test_hints_case(const struct hints_case *hints_case)
+{
+       struct objagg_obj *objagg_obj;
+       struct objagg_hints *hints;
+       struct world world2 = {};
+       struct world world = {};
+       struct objagg *objagg2;
+       struct objagg *objagg;
+       const char *errmsg;
+       int i;
+       int err;
+
+       objagg = objagg_create(&delta_ops, NULL, &world);
+       if (IS_ERR(objagg))
+               return PTR_ERR(objagg);
+
+       for (i = 0; i < hints_case->key_ids_count; i++) {
+               objagg_obj = world_obj_get(&world, objagg,
+                                          hints_case->key_ids[i]);
+               if (IS_ERR(objagg_obj)) {
+                       err = PTR_ERR(objagg_obj);
+                       goto err_world_obj_get;
+               }
+       }
+
+       pr_debug_stats(objagg);
+       err = check_expect_stats(objagg, &hints_case->expect_stats, &errmsg);
+       if (err) {
+               pr_err("Stats: %s\n", errmsg);
+               goto err_check_expect_stats;
+       }
+
+       hints = objagg_hints_get(objagg, OBJAGG_OPT_ALGO_SIMPLE_GREEDY);
+       if (IS_ERR(hints)) {
+               err = PTR_ERR(hints);
+               goto err_hints_get;
+       }
+
+       pr_debug_hints_stats(hints);
+       err = check_expect_hints_stats(hints, &hints_case->expect_stats_hints,
+                                      &errmsg);
+       if (err) {
+               pr_err("Hints stats: %s\n", errmsg);
+               goto err_check_expect_hints_stats;
+       }
+
+       objagg2 = objagg_create(&delta_ops, hints, &world2);
+       if (IS_ERR(objagg2))
+               return PTR_ERR(objagg2);
+
+       for (i = 0; i < hints_case->key_ids_count; i++) {
+               objagg_obj = world_obj_get(&world2, objagg2,
+                                          hints_case->key_ids[i]);
+               if (IS_ERR(objagg_obj)) {
+                       err = PTR_ERR(objagg_obj);
+                       goto err_world2_obj_get;
+               }
+       }
+
+       pr_debug_stats(objagg2);
+       err = check_expect_stats(objagg2, &hints_case->expect_stats_hints,
+                                &errmsg);
+       if (err) {
+               pr_err("Stats2: %s\n", errmsg);
+               goto err_check_expect_stats2;
+       }
+
+       err = 0;
+
+err_check_expect_stats2:
+err_world2_obj_get:
+       for (i--; i >= 0; i--)
+               world_obj_put(&world2, objagg, hints_case->key_ids[i]);
+       objagg_hints_put(hints);
+       objagg_destroy(objagg2);
+       i = hints_case->key_ids_count;
+err_check_expect_hints_stats:
+err_hints_get:
+err_check_expect_stats:
+err_world_obj_get:
+       for (i--; i >= 0; i--)
+               world_obj_put(&world, objagg, hints_case->key_ids[i]);
+
+       objagg_destroy(objagg);
+       return err;
+}
+static int test_hints(void)
+{
+       return test_hints_case(&hints_case);
+}
+
 static int __init test_objagg_init(void)
 {
        int err;
@@ -822,7 +1004,10 @@ static int __init test_objagg_init(void)
        err = test_nodelta();
        if (err)
                return err;
-       return test_delta();
+       err = test_delta();
+       if (err)
+               return err;
+       return test_hints();
 }
 
 static void __exit test_objagg_exit(void)
index 6a8ac76..e52f8ca 100644 (file)
@@ -541,38 +541,45 @@ static unsigned int __init print_ht(struct rhltable *rhlt)
 static int __init test_insert_dup(struct test_obj_rhl *rhl_test_objects,
                                  int cnt, bool slow)
 {
-       struct rhltable rhlt;
+       struct rhltable *rhlt;
        unsigned int i, ret;
        const char *key;
        int err = 0;
 
-       err = rhltable_init(&rhlt, &test_rht_params_dup);
-       if (WARN_ON(err))
+       rhlt = kmalloc(sizeof(*rhlt), GFP_KERNEL);
+       if (WARN_ON(!rhlt))
+               return -EINVAL;
+
+       err = rhltable_init(rhlt, &test_rht_params_dup);
+       if (WARN_ON(err)) {
+               kfree(rhlt);
                return err;
+       }
 
        for (i = 0; i < cnt; i++) {
                rhl_test_objects[i].value.tid = i;
-               key = rht_obj(&rhlt.ht, &rhl_test_objects[i].list_node.rhead);
+               key = rht_obj(&rhlt->ht, &rhl_test_objects[i].list_node.rhead);
                key += test_rht_params_dup.key_offset;
 
                if (slow) {
-                       err = PTR_ERR(rhashtable_insert_slow(&rhlt.ht, key,
+                       err = PTR_ERR(rhashtable_insert_slow(&rhlt->ht, key,
                                                             &rhl_test_objects[i].list_node.rhead));
                        if (err == -EAGAIN)
                                err = 0;
                } else
-                       err = rhltable_insert(&rhlt,
+                       err = rhltable_insert(rhlt,
                                              &rhl_test_objects[i].list_node,
                                              test_rht_params_dup);
                if (WARN(err, "error %d on element %d/%d (%s)\n", err, i, cnt, slow? "slow" : "fast"))
                        goto skip_print;
        }
 
-       ret = print_ht(&rhlt);
+       ret = print_ht(rhlt);
        WARN(ret != cnt, "missing rhltable elements (%d != %d, %s)\n", ret, cnt, slow? "slow" : "fast");
 
 skip_print:
-       rhltable_destroy(&rhlt);
+       rhltable_destroy(rhlt);
+       kfree(rhlt);
 
        return 0;
 }
index 05acd7e..7502964 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -1674,7 +1674,8 @@ static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
                if (!pmd_present(pmd))
                        return 0;
 
-               if (unlikely(pmd_trans_huge(pmd) || pmd_huge(pmd))) {
+               if (unlikely(pmd_trans_huge(pmd) || pmd_huge(pmd) ||
+                            pmd_devmap(pmd))) {
                        /*
                         * NUMA hinting faults need to be handled in the GUP
                         * slowpath for accounting purposes and so that they
index df2e7dd..afef616 100644 (file)
@@ -4268,7 +4268,8 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
                                break;
                        }
                        if (ret & VM_FAULT_RETRY) {
-                               if (nonblocking)
+                               if (nonblocking &&
+                                   !(fault_flags & FAULT_FLAG_RETRY_NOWAIT))
                                        *nonblocking = 0;
                                *nr_pages = 0;
                                /*
index 0a14fcf..e2bb06c 100644 (file)
@@ -5,6 +5,7 @@ UBSAN_SANITIZE_generic.o := n
 UBSAN_SANITIZE_tags.o := n
 KCOV_INSTRUMENT := n
 
+CFLAGS_REMOVE_common.o = -pg
 CFLAGS_REMOVE_generic.o = -pg
 # Function splitter causes unnecessary splits in __asan_load1/__asan_store1
 # see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63533
index 7c72f2a..831be5f 100644 (file)
@@ -372,7 +372,8 @@ static void kill_procs(struct list_head *to_kill, int forcekill, bool fail,
                        if (fail || tk->addr_valid == 0) {
                                pr_err("Memory failure: %#lx: forcibly killing %s:%d because of failure to unmap corrupted page\n",
                                       pfn, tk->tsk->comm, tk->tsk->pid);
-                               force_sig(SIGKILL, tk->tsk);
+                               do_send_sig_info(SIGKILL, SEND_SIG_PRIV,
+                                                tk->tsk, PIDTYPE_PID);
                        }
 
                        /*
index b9a667d..124e794 100644 (file)
@@ -1233,7 +1233,8 @@ static bool is_pageblock_removable_nolock(struct page *page)
 bool is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)
 {
        struct page *page = pfn_to_page(start_pfn);
-       struct page *end_page = page + nr_pages;
+       unsigned long end_pfn = min(start_pfn + nr_pages, zone_end_pfn(page_zone(page)));
+       struct page *end_page = pfn_to_page(end_pfn);
 
        /* Check the starting page of each pageblock within the range */
        for (; page < end_page; page = next_active_pageblock(page)) {
@@ -1273,6 +1274,9 @@ int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn,
                                i++;
                        if (i == MAX_ORDER_NR_PAGES || pfn + i >= end_pfn)
                                continue;
+                       /* Check if we got outside of the zone */
+                       if (zone && !zone_spans_pfn(zone, pfn + i))
+                               return 0;
                        page = pfn_to_page(pfn + i);
                        if (zone && page_zone(page) != zone)
                                return 0;
@@ -1301,23 +1305,27 @@ int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn,
 static unsigned long scan_movable_pages(unsigned long start, unsigned long end)
 {
        unsigned long pfn;
-       struct page *page;
+
        for (pfn = start; pfn < end; pfn++) {
-               if (pfn_valid(pfn)) {
-                       page = pfn_to_page(pfn);
-                       if (PageLRU(page))
-                               return pfn;
-                       if (__PageMovable(page))
-                               return pfn;
-                       if (PageHuge(page)) {
-                               if (hugepage_migration_supported(page_hstate(page)) &&
-                                   page_huge_active(page))
-                                       return pfn;
-                               else
-                                       pfn = round_up(pfn + 1,
-                                               1 << compound_order(page)) - 1;
-                       }
-               }
+               struct page *page, *head;
+               unsigned long skip;
+
+               if (!pfn_valid(pfn))
+                       continue;
+               page = pfn_to_page(pfn);
+               if (PageLRU(page))
+                       return pfn;
+               if (__PageMovable(page))
+                       return pfn;
+
+               if (!PageHuge(page))
+                       continue;
+               head = compound_head(page);
+               if (hugepage_migration_supported(page_hstate(head)) &&
+                   page_huge_active(head))
+                       return pfn;
+               skip = (1 << compound_order(head)) - (page - head);
+               pfn += skip - 1;
        }
        return 0;
 }
@@ -1344,7 +1352,6 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
 {
        unsigned long pfn;
        struct page *page;
-       int not_managed = 0;
        int ret = 0;
        LIST_HEAD(source);
 
@@ -1392,7 +1399,6 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
                else
                        ret = isolate_movable_page(page, ISOLATE_UNEVICTABLE);
                if (!ret) { /* Success */
-                       put_page(page);
                        list_add_tail(&page->lru, &source);
                        if (!__PageMovable(page))
                                inc_node_page_state(page, NR_ISOLATED_ANON +
@@ -1401,22 +1407,10 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
                } else {
                        pr_warn("failed to isolate pfn %lx\n", pfn);
                        dump_page(page, "isolation failed");
-                       put_page(page);
-                       /* Because we don't have big zone->lock. we should
-                          check this again here. */
-                       if (page_count(page)) {
-                               not_managed++;
-                               ret = -EBUSY;
-                               break;
-                       }
                }
+               put_page(page);
        }
        if (!list_empty(&source)) {
-               if (not_managed) {
-                       putback_movable_pages(&source);
-                       goto out;
-               }
-
                /* Allocate a new page from the nearest neighbor node */
                ret = migrate_pages(&source, new_node_page, NULL, 0,
                                        MIGRATE_SYNC, MR_MEMORY_HOTPLUG);
@@ -1429,7 +1423,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
                        putback_movable_pages(&source);
                }
        }
-out:
+
        return ret;
 }
 
@@ -1576,7 +1570,6 @@ static int __ref __offline_pages(unsigned long start_pfn,
           we assume this for now. .*/
        if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start,
                                  &valid_end)) {
-               mem_hotplug_done();
                ret = -EINVAL;
                reason = "multizone range";
                goto failed_removal;
@@ -1591,7 +1584,6 @@ static int __ref __offline_pages(unsigned long start_pfn,
                                       MIGRATE_MOVABLE,
                                       SKIP_HWPOISON | REPORT_FAILURE);
        if (ret) {
-               mem_hotplug_done();
                reason = "failure to isolate range";
                goto failed_removal;
        }
index a16b150..d4fd680 100644 (file)
@@ -709,7 +709,6 @@ static bool buffer_migrate_lock_buffers(struct buffer_head *head,
        /* Simple case, sync compaction */
        if (mode != MIGRATE_ASYNC) {
                do {
-                       get_bh(bh);
                        lock_buffer(bh);
                        bh = bh->b_this_page;
 
@@ -720,18 +719,15 @@ static bool buffer_migrate_lock_buffers(struct buffer_head *head,
 
        /* async case, we cannot block on lock_buffer so use trylock_buffer */
        do {
-               get_bh(bh);
                if (!trylock_buffer(bh)) {
                        /*
                         * We failed to lock the buffer and cannot stall in
                         * async migration. Release the taken locks
                         */
                        struct buffer_head *failed_bh = bh;
-                       put_bh(failed_bh);
                        bh = head;
                        while (bh != failed_bh) {
                                unlock_buffer(bh);
-                               put_bh(bh);
                                bh = bh->b_this_page;
                        }
                        return false;
@@ -818,7 +814,6 @@ unlock_buffers:
        bh = head;
        do {
                unlock_buffer(bh);
-               put_bh(bh);
                bh = bh->b_this_page;
 
        } while (bh != head);
@@ -1135,10 +1130,13 @@ out:
         * If migration is successful, decrease refcount of the newpage
         * which will not free the page because new page owner increased
         * refcounter. As well, if it is LRU page, add the page to LRU
-        * list in here.
+        * list in here. Use the old state of the isolated source page to
+        * determine if we migrated a LRU page. newpage was already unlocked
+        * and possibly modified by its owner - don't rely on the page
+        * state.
         */
        if (rc == MIGRATEPAGE_SUCCESS) {
-               if (unlikely(__PageMovable(newpage)))
+               if (unlikely(!is_lru))
                        put_page(newpage);
                else
                        putback_lru_page(newpage);
index f0e8cd9..26ea863 100644 (file)
@@ -647,8 +647,8 @@ static int oom_reaper(void *unused)
 
 static void wake_oom_reaper(struct task_struct *tsk)
 {
-       /* tsk is already queued? */
-       if (tsk == oom_reaper_list || tsk->oom_reaper_list)
+       /* mm is already queued? */
+       if (test_and_set_bit(MMF_OOM_REAP_QUEUED, &tsk->signal->oom_mm->flags))
                return;
 
        get_task_struct(tsk);
@@ -975,6 +975,13 @@ static void oom_kill_process(struct oom_control *oc, const char *message)
         * still freeing memory.
         */
        read_lock(&tasklist_lock);
+
+       /*
+        * The task 'p' might have already exited before reaching here. The
+        * put_task_struct() will free task_struct 'p' while the loop still try
+        * to access the field of 'p', so, get an extra reference.
+        */
+       get_task_struct(p);
        for_each_thread(p, t) {
                list_for_each_entry(child, &t->children, sibling) {
                        unsigned int child_points;
@@ -994,6 +1001,7 @@ static void oom_kill_process(struct oom_control *oc, const char *message)
                        }
                }
        }
+       put_task_struct(p);
        read_unlock(&tasklist_lock);
 
        /*
index d295c9b..46285d2 100644 (file)
@@ -4675,11 +4675,11 @@ refill:
                /* Even if we own the page, we do not use atomic_set().
                 * This would break get_page_unless_zero() users.
                 */
-               page_ref_add(page, size - 1);
+               page_ref_add(page, size);
 
                /* reset page count bias and offset to start of new frag */
                nc->pfmemalloc = page_is_pfmemalloc(page);
-               nc->pagecnt_bias = size;
+               nc->pagecnt_bias = size + 1;
                nc->offset = size;
        }
 
@@ -4695,10 +4695,10 @@ refill:
                size = nc->size;
 #endif
                /* OK, page count is 0, we can safely set it */
-               set_page_count(page, size);
+               set_page_count(page, size + 1);
 
                /* reset page count bias and offset to start of new frag */
-               nc->pagecnt_bias = size;
+               nc->pagecnt_bias = size + 1;
                offset = size - fragsz;
        }
 
@@ -5701,18 +5701,6 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
                        cond_resched();
                }
        }
-#ifdef CONFIG_SPARSEMEM
-       /*
-        * If the zone does not span the rest of the section then
-        * we should at least initialize those pages. Otherwise we
-        * could blow up on a poisoned page in some paths which depend
-        * on full sections being initialized (e.g. memory hotplug).
-        */
-       while (end_pfn % PAGES_PER_SECTION) {
-               __init_single_page(pfn_to_page(end_pfn), end_pfn, zone, nid);
-               end_pfn++;
-       }
-#endif
 }
 
 #ifdef CONFIG_ZONE_DEVICE
index ae44f7a..8c78b8d 100644 (file)
@@ -398,10 +398,8 @@ void __init page_ext_init(void)
                         * We know some arch can have a nodes layout such as
                         * -------------pfn-------------->
                         * N0 | N1 | N2 | N0 | N1 | N2|....
-                        *
-                        * Take into account DEFERRED_STRUCT_PAGE_INIT.
                         */
-                       if (early_pfn_to_nid(pfn) != nid)
+                       if (pfn_to_nid(pfn) != nid)
                                continue;
                        if (init_section_page_ext(pfn, nid))
                                goto oom;
index a714c4f..e979705 100644 (file)
@@ -491,16 +491,6 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl,
                delta = freeable / 2;
        }
 
-       /*
-        * Make sure we apply some minimal pressure on default priority
-        * even on small cgroups. Stale objects are not only consuming memory
-        * by themselves, but can also hold a reference to a dying cgroup,
-        * preventing it from being reclaimed. A dying cgroup with all
-        * corresponding structures like per-cpu stats and kmem caches
-        * can be really big, so it may lead to a significant waste of memory.
-        */
-       delta = max_t(unsigned long long, delta, min(freeable, batch_size));
-
        total_scan += delta;
        if (total_scan < 0) {
                pr_err("shrink_slab: %pF negative objects to delete nr=%ld\n",
index 5cb9de1..62da614 100644 (file)
@@ -403,7 +403,7 @@ config LWTUNNEL
 
 config LWTUNNEL_BPF
        bool "Execute BPF program as route nexthop action"
-       depends on LWTUNNEL
+       depends on LWTUNNEL && INET
        default y if LWTUNNEL=y
        ---help---
          Allows to run BPF programs as a nexthop action following a route
index 0b0495a..d79221f 100644 (file)
@@ -134,7 +134,8 @@ static void vcc_seq_stop(struct seq_file *seq, void *v)
 static void *vcc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
        v = vcc_walk(seq, 1);
-       *pos += !!PTR_ERR(v);
+       if (v)
+               (*pos)++;
        return v;
 }
 
index c386e69..a31db5e 100644 (file)
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-# Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+# Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
 #
 # Marek Lindner, Simon Wunderlich
 #
index 9b58160..a887ecc 100644 (file)
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-# Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+# Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
 #
 # Marek Lindner, Simon Wunderlich
 #
index ea309ad..7b7e156 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index 534b790..25e7bb5 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2011-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2011-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Linus Lüssing
  *
index f97e566..de61091 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index 3dc6a7a..785f666 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index 90e33f8..445594e 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2013-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2013-2019  B.A.T.M.A.N. contributors:
  *
  * Linus Lüssing, Marek Lindner
  *
index ec4a2a5..465a4fc 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2011-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2011-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Linus Lüssing
  *
index e8090f0..a9b7919 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2011-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2011-2019  B.A.T.M.A.N. contributors:
  *
  * Linus Lüssing, Marek Lindner
  *
@@ -104,6 +104,9 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
 
                ret = cfg80211_get_station(real_netdev, neigh->addr, &sinfo);
 
+               /* free the TID stats immediately */
+               cfg80211_sinfo_release_content(&sinfo);
+
                dev_put(real_netdev);
                if (ret == -ENOENT) {
                        /* Node is not associated anymore! It would be
index e8c7b7f..75f189e 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2013-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2013-2019  B.A.T.M.A.N. contributors:
  *
  * Linus Lüssing, Marek Lindner
  *
index 2948b41..c9698ad 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2013-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2013-2019  B.A.T.M.A.N. contributors:
  *
  * Antonio Quartulli
  *
index e5be14c..f67cf7e 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2013-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2013-2019  B.A.T.M.A.N. contributors:
  *
  * Antonio Quartulli
  *
index a296a4d..63e134e 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2006-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2006-2019  B.A.T.M.A.N. contributors:
  *
  * Simon Wunderlich, Marek Lindner
  *
index 48f6832..f3a05ad 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2006-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2006-2019  B.A.T.M.A.N. contributors:
  *
  * Simon Wunderlich, Marek Lindner
  *
index 5fdde29..ef39aab 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2011-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2011-2019  B.A.T.M.A.N. contributors:
  *
  * Simon Wunderlich
  *
index 71f95a3..31771c7 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2011-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2011-2019  B.A.T.M.A.N. contributors:
  *
  * Simon Wunderlich
  *
index d4a7702..3b9d1ad 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2010-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2010-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
index 8de018e..c0b8694 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2010-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2010-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
index b9ffe18..310a4f3 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2011-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2011-2019  B.A.T.M.A.N. contributors:
  *
  * Antonio Quartulli
  *
@@ -19,6 +19,7 @@
 #include "distributed-arp-table.h"
 #include "main.h"
 
+#include <asm/unaligned.h>
 #include <linux/atomic.h>
 #include <linux/bitops.h>
 #include <linux/byteorder/generic.h>
@@ -29,6 +30,7 @@
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
 #include <linux/in.h>
+#include <linux/ip.h>
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/kref.h>
@@ -42,6 +44,7 @@
 #include <linux/spinlock.h>
 #include <linux/stddef.h>
 #include <linux/string.h>
+#include <linux/udp.h>
 #include <linux/workqueue.h>
 #include <net/arp.h>
 #include <net/genetlink.h>
 #include "translation-table.h"
 #include "tvlv.h"
 
+enum batadv_bootpop {
+       BATADV_BOOTREPLY        = 2,
+};
+
+enum batadv_boothtype {
+       BATADV_HTYPE_ETHERNET   = 1,
+};
+
+enum batadv_dhcpoptioncode {
+       BATADV_DHCP_OPT_PAD             = 0,
+       BATADV_DHCP_OPT_MSG_TYPE        = 53,
+       BATADV_DHCP_OPT_END             = 255,
+};
+
+enum batadv_dhcptype {
+       BATADV_DHCPACK          = 5,
+};
+
+/* { 99, 130, 83, 99 } */
+#define BATADV_DHCP_MAGIC 1669485411
+
+struct batadv_dhcp_packet {
+       __u8 op;
+       __u8 htype;
+       __u8 hlen;
+       __u8 hops;
+       __be32 xid;
+       __be16 secs;
+       __be16 flags;
+       __be32 ciaddr;
+       __be32 yiaddr;
+       __be32 siaddr;
+       __be32 giaddr;
+       __u8 chaddr[16];
+       __u8 sname[64];
+       __u8 file[128];
+       __be32 magic;
+       __u8 options[0];
+};
+
+#define BATADV_DHCP_YIADDR_LEN sizeof(((struct batadv_dhcp_packet *)0)->yiaddr)
+#define BATADV_DHCP_CHADDR_LEN sizeof(((struct batadv_dhcp_packet *)0)->chaddr)
+
 static void batadv_dat_purge(struct work_struct *work);
 
 /**
@@ -1440,6 +1486,361 @@ out:
 }
 
 /**
+ * batadv_dat_check_dhcp_ipudp() - check skb for IP+UDP headers valid for DHCP
+ * @skb: the packet to check
+ * @ip_src: a buffer to store the IPv4 source address in
+ *
+ * Checks whether the given skb has an IP and UDP header valid for a DHCP
+ * message from a DHCP server. And if so, stores the IPv4 source address in
+ * the provided buffer.
+ *
+ * Return: True if valid, false otherwise.
+ */
+static bool
+batadv_dat_check_dhcp_ipudp(struct sk_buff *skb, __be32 *ip_src)
+{
+       unsigned int offset = skb_network_offset(skb);
+       struct udphdr *udphdr, _udphdr;
+       struct iphdr *iphdr, _iphdr;
+
+       iphdr = skb_header_pointer(skb, offset, sizeof(_iphdr), &_iphdr);
+       if (!iphdr || iphdr->version != 4 || iphdr->ihl * 4 < sizeof(_iphdr))
+               return false;
+
+       if (iphdr->protocol != IPPROTO_UDP)
+               return false;
+
+       offset += iphdr->ihl * 4;
+       skb_set_transport_header(skb, offset);
+
+       udphdr = skb_header_pointer(skb, offset, sizeof(_udphdr), &_udphdr);
+       if (!udphdr || udphdr->source != htons(67))
+               return false;
+
+       *ip_src = get_unaligned(&iphdr->saddr);
+
+       return true;
+}
+
+/**
+ * batadv_dat_check_dhcp() - examine packet for valid DHCP message
+ * @skb: the packet to check
+ * @proto: ethernet protocol hint (behind a potential vlan)
+ * @ip_src: a buffer to store the IPv4 source address in
+ *
+ * Checks whether the given skb is a valid DHCP packet. And if so, stores the
+ * IPv4 source address in the provided buffer.
+ *
+ * Caller needs to ensure that the skb network header is set correctly.
+ *
+ * Return: If skb is a valid DHCP packet, then returns its op code
+ * (e.g. BOOTREPLY vs. BOOTREQUEST). Otherwise returns -EINVAL.
+ */
+static int
+batadv_dat_check_dhcp(struct sk_buff *skb, __be16 proto, __be32 *ip_src)
+{
+       __be32 *magic, _magic;
+       unsigned int offset;
+       struct {
+               __u8 op;
+               __u8 htype;
+               __u8 hlen;
+               __u8 hops;
+       } *dhcp_h, _dhcp_h;
+
+       if (proto != htons(ETH_P_IP))
+               return -EINVAL;
+
+       if (!batadv_dat_check_dhcp_ipudp(skb, ip_src))
+               return -EINVAL;
+
+       offset = skb_transport_offset(skb) + sizeof(struct udphdr);
+       if (skb->len < offset + sizeof(struct batadv_dhcp_packet))
+               return -EINVAL;
+
+       dhcp_h = skb_header_pointer(skb, offset, sizeof(_dhcp_h), &_dhcp_h);
+       if (!dhcp_h || dhcp_h->htype != BATADV_HTYPE_ETHERNET ||
+           dhcp_h->hlen != ETH_ALEN)
+               return -EINVAL;
+
+       offset += offsetof(struct batadv_dhcp_packet, magic);
+
+       magic = skb_header_pointer(skb, offset, sizeof(_magic), &_magic);
+       if (!magic || get_unaligned(magic) != htonl(BATADV_DHCP_MAGIC))
+               return -EINVAL;
+
+       return dhcp_h->op;
+}
+
+/**
+ * batadv_dat_get_dhcp_message_type() - get message type of a DHCP packet
+ * @skb: the DHCP packet to parse
+ *
+ * Iterates over the DHCP options of the given DHCP packet to find a
+ * DHCP Message Type option and parse it.
+ *
+ * Caller needs to ensure that the given skb is a valid DHCP packet and
+ * that the skb transport header is set correctly.
+ *
+ * Return: The found DHCP message type value, if found. -EINVAL otherwise.
+ */
+static int batadv_dat_get_dhcp_message_type(struct sk_buff *skb)
+{
+       unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr);
+       u8 *type, _type;
+       struct {
+               u8 type;
+               u8 len;
+       } *tl, _tl;
+
+       offset += sizeof(struct batadv_dhcp_packet);
+
+       while ((tl = skb_header_pointer(skb, offset, sizeof(_tl), &_tl))) {
+               if (tl->type == BATADV_DHCP_OPT_MSG_TYPE)
+                       break;
+
+               if (tl->type == BATADV_DHCP_OPT_END)
+                       break;
+
+               if (tl->type == BATADV_DHCP_OPT_PAD)
+                       offset++;
+               else
+                       offset += tl->len + sizeof(_tl);
+       }
+
+       /* Option Overload Code not supported */
+       if (!tl || tl->type != BATADV_DHCP_OPT_MSG_TYPE ||
+           tl->len != sizeof(_type))
+               return -EINVAL;
+
+       offset += sizeof(_tl);
+
+       type = skb_header_pointer(skb, offset, sizeof(_type), &_type);
+       if (!type)
+               return -EINVAL;
+
+       return *type;
+}
+
+/**
+ * batadv_dat_get_dhcp_yiaddr() - get yiaddr from a DHCP packet
+ * @skb: the DHCP packet to parse
+ * @buf: a buffer to store the yiaddr in
+ *
+ * Caller needs to ensure that the given skb is a valid DHCP packet and
+ * that the skb transport header is set correctly.
+ *
+ * Return: True on success, false otherwise.
+ */
+static bool batadv_dat_dhcp_get_yiaddr(struct sk_buff *skb, __be32 *buf)
+{
+       unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr);
+       __be32 *yiaddr;
+
+       offset += offsetof(struct batadv_dhcp_packet, yiaddr);
+       yiaddr = skb_header_pointer(skb, offset, BATADV_DHCP_YIADDR_LEN, buf);
+
+       if (!yiaddr)
+               return false;
+
+       if (yiaddr != buf)
+               *buf = get_unaligned(yiaddr);
+
+       return true;
+}
+
+/**
+ * batadv_dat_get_dhcp_chaddr() - get chaddr from a DHCP packet
+ * @skb: the DHCP packet to parse
+ * @buf: a buffer to store the chaddr in
+ *
+ * Caller needs to ensure that the given skb is a valid DHCP packet and
+ * that the skb transport header is set correctly.
+ *
+ * Return: True on success, false otherwise
+ */
+static bool batadv_dat_get_dhcp_chaddr(struct sk_buff *skb, u8 *buf)
+{
+       unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr);
+       u8 *chaddr;
+
+       offset += offsetof(struct batadv_dhcp_packet, chaddr);
+       chaddr = skb_header_pointer(skb, offset, BATADV_DHCP_CHADDR_LEN, buf);
+
+       if (!chaddr)
+               return false;
+
+       if (chaddr != buf)
+               memcpy(buf, chaddr, BATADV_DHCP_CHADDR_LEN);
+
+       return true;
+}
+
+/**
+ * batadv_dat_put_dhcp() - puts addresses from a DHCP packet into the DHT and
+ *  DAT cache
+ * @bat_priv: the bat priv with all the soft interface information
+ * @chaddr: the DHCP client MAC address
+ * @yiaddr: the DHCP client IP address
+ * @hw_dst: the DHCP server MAC address
+ * @ip_dst: the DHCP server IP address
+ * @vid: VLAN identifier
+ *
+ * Adds given MAC/IP pairs to the local DAT cache and propagates them further
+ * into the DHT.
+ *
+ * For the DHT propagation, client MAC + IP will appear as the ARP Reply
+ * transmitter (and hw_dst/ip_dst as the target).
+ */
+static void batadv_dat_put_dhcp(struct batadv_priv *bat_priv, u8 *chaddr,
+                               __be32 yiaddr, u8 *hw_dst, __be32 ip_dst,
+                               unsigned short vid)
+{
+       struct sk_buff *skb;
+
+       skb = batadv_dat_arp_create_reply(bat_priv, yiaddr, ip_dst, chaddr,
+                                         hw_dst, vid);
+       if (!skb)
+               return;
+
+       skb_set_network_header(skb, ETH_HLEN);
+
+       batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid);
+       batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
+
+       batadv_dat_send_data(bat_priv, skb, yiaddr, vid, BATADV_P_DAT_DHT_PUT);
+       batadv_dat_send_data(bat_priv, skb, ip_dst, vid, BATADV_P_DAT_DHT_PUT);
+
+       consume_skb(skb);
+
+       batadv_dbg(BATADV_DBG_DAT, bat_priv,
+                  "Snooped from outgoing DHCPACK (server address): %pI4, %pM (vid: %i)\n",
+                  &ip_dst, hw_dst, batadv_print_vid(vid));
+       batadv_dbg(BATADV_DBG_DAT, bat_priv,
+                  "Snooped from outgoing DHCPACK (client address): %pI4, %pM (vid: %i)\n",
+                  &yiaddr, chaddr, batadv_print_vid(vid));
+}
+
+/**
+ * batadv_dat_check_dhcp_ack() - examine packet for valid DHCP message
+ * @skb: the packet to check
+ * @proto: ethernet protocol hint (behind a potential vlan)
+ * @ip_src: a buffer to store the IPv4 source address in
+ * @chaddr: a buffer to store the DHCP Client Hardware Address in
+ * @yiaddr: a buffer to store the DHCP Your IP Address in
+ *
+ * Checks whether the given skb is a valid DHCPACK. And if so, stores the
+ * IPv4 server source address (ip_src), client MAC address (chaddr) and client
+ * IPv4 address (yiaddr) in the provided buffers.
+ *
+ * Caller needs to ensure that the skb network header is set correctly.
+ *
+ * Return: True if the skb is a valid DHCPACK. False otherwise.
+ */
+static bool
+batadv_dat_check_dhcp_ack(struct sk_buff *skb, __be16 proto, __be32 *ip_src,
+                         u8 *chaddr, __be32 *yiaddr)
+{
+       int type;
+
+       type = batadv_dat_check_dhcp(skb, proto, ip_src);
+       if (type != BATADV_BOOTREPLY)
+               return false;
+
+       type = batadv_dat_get_dhcp_message_type(skb);
+       if (type != BATADV_DHCPACK)
+               return false;
+
+       if (!batadv_dat_dhcp_get_yiaddr(skb, yiaddr))
+               return false;
+
+       if (!batadv_dat_get_dhcp_chaddr(skb, chaddr))
+               return false;
+
+       return true;
+}
+
+/**
+ * batadv_dat_snoop_outgoing_dhcp_ack() - snoop DHCPACK and fill DAT with it
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the packet to snoop
+ * @proto: ethernet protocol hint (behind a potential vlan)
+ * @vid: VLAN identifier
+ *
+ * This function first checks whether the given skb is a valid DHCPACK. If
+ * so then its source MAC and IP as well as its DHCP Client Hardware Address
+ * field and DHCP Your IP Address field are added to the local DAT cache and
+ * propagated into the DHT.
+ *
+ * Caller needs to ensure that the skb mac and network headers are set
+ * correctly.
+ */
+void batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv,
+                                       struct sk_buff *skb,
+                                       __be16 proto,
+                                       unsigned short vid)
+{
+       u8 chaddr[BATADV_DHCP_CHADDR_LEN];
+       __be32 ip_src, yiaddr;
+
+       if (!atomic_read(&bat_priv->distributed_arp_table))
+               return;
+
+       if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr))
+               return;
+
+       batadv_dat_put_dhcp(bat_priv, chaddr, yiaddr, eth_hdr(skb)->h_source,
+                           ip_src, vid);
+}
+
+/**
+ * batadv_dat_snoop_incoming_dhcp_ack() - snoop DHCPACK and fill DAT cache
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the packet to snoop
+ * @hdr_size: header size, up to the tail of the batman-adv header
+ *
+ * This function first checks whether the given skb is a valid DHCPACK. If
+ * so then its source MAC and IP as well as its DHCP Client Hardware Address
+ * field and DHCP Your IP Address field are added to the local DAT cache.
+ */
+void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
+                                       struct sk_buff *skb, int hdr_size)
+{
+       u8 chaddr[BATADV_DHCP_CHADDR_LEN];
+       struct ethhdr *ethhdr;
+       __be32 ip_src, yiaddr;
+       unsigned short vid;
+       __be16 proto;
+       u8 *hw_src;
+
+       if (!atomic_read(&bat_priv->distributed_arp_table))
+               return;
+
+       if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN)))
+               return;
+
+       ethhdr = (struct ethhdr *)(skb->data + hdr_size);
+       skb_set_network_header(skb, hdr_size + ETH_HLEN);
+       proto = ethhdr->h_proto;
+
+       if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr))
+               return;
+
+       hw_src = ethhdr->h_source;
+       vid = batadv_dat_get_vid(skb, &hdr_size);
+
+       batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid);
+       batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
+
+       batadv_dbg(BATADV_DBG_DAT, bat_priv,
+                  "Snooped from incoming DHCPACK (server address): %pI4, %pM (vid: %i)\n",
+                  &ip_src, hw_src, batadv_print_vid(vid));
+       batadv_dbg(BATADV_DBG_DAT, bat_priv,
+                  "Snooped from incoming DHCPACK (client address): %pI4, %pM (vid: %i)\n",
+                  &yiaddr, chaddr, batadv_print_vid(vid));
+}
+
+/**
  * batadv_dat_drop_broadcast_packet() - check if an ARP request has to be
  *  dropped (because the node has already obtained the reply via DAT) or not
  * @bat_priv: the bat priv with all the soft interface information
index a045960..68c0ff3 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2011-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2011-2019  B.A.T.M.A.N. contributors:
  *
  * Antonio Quartulli
  *
@@ -46,6 +46,12 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
                                         struct sk_buff *skb);
 bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
                                         struct sk_buff *skb, int hdr_size);
+void batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv,
+                                       struct sk_buff *skb,
+                                       __be16 proto,
+                                       unsigned short vid);
+void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
+                                       struct sk_buff *skb, int hdr_size);
 bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
                                      struct batadv_forw_packet *forw_packet);
 
@@ -140,6 +146,19 @@ batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
        return false;
 }
 
+static inline void
+batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv,
+                                  struct sk_buff *skb, __be16 proto,
+                                  unsigned short vid)
+{
+}
+
+static inline void
+batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
+                                  struct sk_buff *skb, int hdr_size)
+{
+}
+
 static inline bool
 batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
                                 struct batadv_forw_packet *forw_packet)
index 5b71a28..b506d15 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2013-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2013-2019  B.A.T.M.A.N. contributors:
  *
  * Martin Hundebøll <martin@hundeboll.net>
  *
index 944512e..abdac26 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2013-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2013-2019  B.A.T.M.A.N. contributors:
  *
  * Martin Hundebøll <martin@hundeboll.net>
  *
index 9d8e5ed..f5811f6 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2009-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2009-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
@@ -47,7 +47,6 @@
 #include <uapi/linux/batadv_packet.h>
 #include <uapi/linux/batman_adv.h>
 
-#include "gateway_common.h"
 #include "hard-interface.h"
 #include "log.h"
 #include "netlink.h"
index f0b86fc..b5732c8 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2009-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2009-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
index 936c107..e064de4 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2009-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2009-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
@@ -28,6 +28,7 @@
 #include <linux/stddef.h>
 #include <linux/string.h>
 #include <uapi/linux/batadv_packet.h>
+#include <uapi/linux/batman_adv.h>
 
 #include "gateway_client.h"
 #include "log.h"
index 80afb27..128467a 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2009-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2009-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
 
 struct net_device;
 
-enum batadv_gw_modes {
-       BATADV_GW_MODE_OFF,
-       BATADV_GW_MODE_CLIENT,
-       BATADV_GW_MODE_SERVER,
-};
-
 /**
  * enum batadv_bandwidth_units - bandwidth unit types
  */
index 508f441..96ef7c7 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
@@ -20,7 +20,6 @@
 #include "main.h"
 
 #include <linux/atomic.h>
-#include <linux/bug.h>
 #include <linux/byteorder/generic.h>
 #include <linux/errno.h>
 #include <linux/gfp.h>
@@ -179,8 +178,10 @@ static bool batadv_is_on_batman_iface(const struct net_device *net_dev)
        parent_dev = __dev_get_by_index((struct net *)parent_net,
                                        dev_get_iflink(net_dev));
        /* if we got a NULL parent_dev there is something broken.. */
-       if (WARN(!parent_dev, "Cannot find parent device"))
+       if (!parent_dev) {
+               pr_err("Cannot find parent device\n");
                return false;
+       }
 
        if (batadv_mutual_parents(net_dev, net, parent_dev, parent_net))
                return false;
index d1c0f61..48de28c 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index 9194f4d..56a08ce 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2006-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2006-2019  B.A.T.M.A.N. contributors:
  *
  * Simon Wunderlich, Marek Lindner
  *
index 0e36fa1..37507b6 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2006-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2006-2019  B.A.T.M.A.N. contributors:
  *
  * Simon Wunderlich, Marek Lindner
  *
index 6d58597..9859aba 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
index 958be22..5f89265 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
index 75f602e..3e610df 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2010-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2010-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
index 35f4f39..660e9bc 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index d1ed839..7575087 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index b572066..3ed669d 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
@@ -25,7 +25,7 @@
 #define BATADV_DRIVER_DEVICE "batman-adv"
 
 #ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2019.0"
+#define BATADV_SOURCE_VERSION "2019.1"
 #endif
 
 /* B.A.T.M.A.N. parameters */
index 1dd70f0..f91b1b6 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2014-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2014-2019  B.A.T.M.A.N. contributors:
  *
  * Linus Lüssing
  *
index 3b04ab1..466013f 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2014-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2014-2019  B.A.T.M.A.N. contributors:
  *
  * Linus Lüssing
  *
index 2dc3304..67a58da 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2016-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2016-2019  B.A.T.M.A.N. contributors:
  *
  * Matthias Schiffer
  *
 #include "main.h"
 
 #include <linux/atomic.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
 #include <linux/byteorder/generic.h>
 #include <linux/cache.h>
+#include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/export.h>
 #include <linux/genetlink.h>
 #include <linux/gfp.h>
 #include <linux/if_ether.h>
+#include <linux/if_vlan.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include "bridge_loop_avoidance.h"
 #include "distributed-arp-table.h"
 #include "gateway_client.h"
+#include "gateway_common.h"
 #include "hard-interface.h"
+#include "log.h"
 #include "multicast.h"
+#include "network-coding.h"
 #include "originator.h"
 #include "soft-interface.h"
 #include "tp_meter.h"
 #include "translation-table.h"
 
+struct net;
+
 struct genl_family batadv_netlink_family;
 
 /* multicast groups */
 enum batadv_netlink_multicast_groups {
+       BATADV_NL_MCGRP_CONFIG,
        BATADV_NL_MCGRP_TPMETER,
 };
 
+/**
+ * enum batadv_genl_ops_flags - flags for genl_ops's internal_flags
+ */
+enum batadv_genl_ops_flags {
+       /**
+        * @BATADV_FLAG_NEED_MESH: request requires valid soft interface in
+        *  attribute BATADV_ATTR_MESH_IFINDEX and expects a pointer to it to be
+        *  saved in info->user_ptr[0]
+        */
+       BATADV_FLAG_NEED_MESH = BIT(0),
+
+       /**
+        * @BATADV_FLAG_NEED_HARDIF: request requires valid hard interface in
+        *  attribute BATADV_ATTR_HARD_IFINDEX and expects a pointer to it to be
+        *  saved in info->user_ptr[1]
+        */
+       BATADV_FLAG_NEED_HARDIF = BIT(1),
+
+       /**
+        * @BATADV_FLAG_NEED_VLAN: request requires valid vlan in
+        *  attribute BATADV_ATTR_VLANID and expects a pointer to it to be
+        *  saved in info->user_ptr[1]
+        */
+       BATADV_FLAG_NEED_VLAN = BIT(2),
+};
+
 static const struct genl_multicast_group batadv_netlink_mcgrps[] = {
+       [BATADV_NL_MCGRP_CONFIG] = { .name = BATADV_NL_MCAST_GROUP_CONFIG },
        [BATADV_NL_MCGRP_TPMETER] = { .name = BATADV_NL_MCAST_GROUP_TPMETER },
 };
 
@@ -104,6 +141,26 @@ static const struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
        [BATADV_ATTR_DAT_CACHE_VID]             = { .type = NLA_U16 },
        [BATADV_ATTR_MCAST_FLAGS]               = { .type = NLA_U32 },
        [BATADV_ATTR_MCAST_FLAGS_PRIV]          = { .type = NLA_U32 },
+       [BATADV_ATTR_VLANID]                    = { .type = NLA_U16 },
+       [BATADV_ATTR_AGGREGATED_OGMS_ENABLED]   = { .type = NLA_U8 },
+       [BATADV_ATTR_AP_ISOLATION_ENABLED]      = { .type = NLA_U8 },
+       [BATADV_ATTR_ISOLATION_MARK]            = { .type = NLA_U32 },
+       [BATADV_ATTR_ISOLATION_MASK]            = { .type = NLA_U32 },
+       [BATADV_ATTR_BONDING_ENABLED]           = { .type = NLA_U8 },
+       [BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED]     = { .type = NLA_U8 },
+       [BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED]     = { .type = NLA_U8 },
+       [BATADV_ATTR_FRAGMENTATION_ENABLED]     = { .type = NLA_U8 },
+       [BATADV_ATTR_GW_BANDWIDTH_DOWN]         = { .type = NLA_U32 },
+       [BATADV_ATTR_GW_BANDWIDTH_UP]           = { .type = NLA_U32 },
+       [BATADV_ATTR_GW_MODE]                   = { .type = NLA_U8 },
+       [BATADV_ATTR_GW_SEL_CLASS]              = { .type = NLA_U32 },
+       [BATADV_ATTR_HOP_PENALTY]               = { .type = NLA_U8 },
+       [BATADV_ATTR_LOG_LEVEL]                 = { .type = NLA_U32 },
+       [BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED]      = { .type = NLA_U8 },
+       [BATADV_ATTR_NETWORK_CODING_ENABLED]    = { .type = NLA_U8 },
+       [BATADV_ATTR_ORIG_INTERVAL]             = { .type = NLA_U32 },
+       [BATADV_ATTR_ELP_INTERVAL]              = { .type = NLA_U32 },
+       [BATADV_ATTR_THROUGHPUT_OVERRIDE]       = { .type = NLA_U32 },
 };
 
 /**
@@ -122,20 +179,75 @@ batadv_netlink_get_ifindex(const struct nlmsghdr *nlh, int attrtype)
 }
 
 /**
- * batadv_netlink_mesh_info_put() - fill in generic information about mesh
- *  interface
- * @msg: netlink message to be sent back
- * @soft_iface: interface for which the data should be taken
+ * batadv_netlink_mesh_fill_ap_isolation() - Add ap_isolation softif attribute
+ * @msg: Netlink message to dump into
+ * @bat_priv: the bat priv with all the soft interface information
  *
- * Return: 0 on success, < 0 on error
+ * Return: 0 on success or negative error number in case of failure
  */
-static int
-batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface)
+static int batadv_netlink_mesh_fill_ap_isolation(struct sk_buff *msg,
+                                                struct batadv_priv *bat_priv)
+{
+       struct batadv_softif_vlan *vlan;
+       u8 ap_isolation;
+
+       vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS);
+       if (!vlan)
+               return 0;
+
+       ap_isolation = atomic_read(&vlan->ap_isolation);
+       batadv_softif_vlan_put(vlan);
+
+       return nla_put_u8(msg, BATADV_ATTR_AP_ISOLATION_ENABLED,
+                         !!ap_isolation);
+}
+
+/**
+ * batadv_option_set_ap_isolation() - Set ap_isolation from genl msg
+ * @attr: parsed BATADV_ATTR_AP_ISOLATION_ENABLED attribute
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_netlink_set_mesh_ap_isolation(struct nlattr *attr,
+                                               struct batadv_priv *bat_priv)
+{
+       struct batadv_softif_vlan *vlan;
+
+       vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS);
+       if (!vlan)
+               return -ENOENT;
+
+       atomic_set(&vlan->ap_isolation, !!nla_get_u8(attr));
+       batadv_softif_vlan_put(vlan);
+
+       return 0;
+}
+
+/**
+ * batadv_netlink_mesh_fill() - Fill message with mesh attributes
+ * @msg: Netlink message to dump into
+ * @bat_priv: the bat priv with all the soft interface information
+ * @cmd: type of message to generate
+ * @portid: Port making netlink request
+ * @seq: sequence number for message
+ * @flags: Additional flags for message
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_netlink_mesh_fill(struct sk_buff *msg,
+                                   struct batadv_priv *bat_priv,
+                                   enum batadv_nl_commands cmd,
+                                   u32 portid, u32 seq, int flags)
 {
-       struct batadv_priv *bat_priv = netdev_priv(soft_iface);
+       struct net_device *soft_iface = bat_priv->soft_iface;
        struct batadv_hard_iface *primary_if = NULL;
        struct net_device *hard_iface;
-       int ret = -ENOBUFS;
+       void *hdr;
+
+       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, flags, cmd);
+       if (!hdr)
+               return -ENOBUFS;
 
        if (nla_put_string(msg, BATADV_ATTR_VERSION, BATADV_SOURCE_VERSION) ||
            nla_put_string(msg, BATADV_ATTR_ALGO_NAME,
@@ -146,16 +258,16 @@ batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface)
                    soft_iface->dev_addr) ||
            nla_put_u8(msg, BATADV_ATTR_TT_TTVN,
                       (u8)atomic_read(&bat_priv->tt.vn)))
-               goto out;
+               goto nla_put_failure;
 
 #ifdef CONFIG_BATMAN_ADV_BLA
        if (nla_put_u16(msg, BATADV_ATTR_BLA_CRC,
                        ntohs(bat_priv->bla.claim_dest.group)))
-               goto out;
+               goto nla_put_failure;
 #endif
 
        if (batadv_mcast_mesh_info_put(msg, bat_priv))
-               goto out;
+               goto nla_put_failure;
 
        primary_if = batadv_primary_if_get_selected(bat_priv);
        if (primary_if && primary_if->if_status == BATADV_IF_ACTIVE) {
@@ -167,77 +279,345 @@ batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface)
                                   hard_iface->name) ||
                    nla_put(msg, BATADV_ATTR_HARD_ADDRESS, ETH_ALEN,
                            hard_iface->dev_addr))
-                       goto out;
+                       goto nla_put_failure;
        }
 
-       ret = 0;
+       if (nla_put_u8(msg, BATADV_ATTR_AGGREGATED_OGMS_ENABLED,
+                      !!atomic_read(&bat_priv->aggregated_ogms)))
+               goto nla_put_failure;
+
+       if (batadv_netlink_mesh_fill_ap_isolation(msg, bat_priv))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, BATADV_ATTR_ISOLATION_MARK,
+                       bat_priv->isolation_mark))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, BATADV_ATTR_ISOLATION_MASK,
+                       bat_priv->isolation_mark_mask))
+               goto nla_put_failure;
+
+       if (nla_put_u8(msg, BATADV_ATTR_BONDING_ENABLED,
+                      !!atomic_read(&bat_priv->bonding)))
+               goto nla_put_failure;
+
+#ifdef CONFIG_BATMAN_ADV_BLA
+       if (nla_put_u8(msg, BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED,
+                      !!atomic_read(&bat_priv->bridge_loop_avoidance)))
+               goto nla_put_failure;
+#endif /* CONFIG_BATMAN_ADV_BLA */
+
+#ifdef CONFIG_BATMAN_ADV_DAT
+       if (nla_put_u8(msg, BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED,
+                      !!atomic_read(&bat_priv->distributed_arp_table)))
+               goto nla_put_failure;
+#endif /* CONFIG_BATMAN_ADV_DAT */
+
+       if (nla_put_u8(msg, BATADV_ATTR_FRAGMENTATION_ENABLED,
+                      !!atomic_read(&bat_priv->fragmentation)))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, BATADV_ATTR_GW_BANDWIDTH_DOWN,
+                       atomic_read(&bat_priv->gw.bandwidth_down)))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, BATADV_ATTR_GW_BANDWIDTH_UP,
+                       atomic_read(&bat_priv->gw.bandwidth_up)))
+               goto nla_put_failure;
+
+       if (nla_put_u8(msg, BATADV_ATTR_GW_MODE,
+                      atomic_read(&bat_priv->gw.mode)))
+               goto nla_put_failure;
+
+       if (bat_priv->algo_ops->gw.get_best_gw_node &&
+           bat_priv->algo_ops->gw.is_eligible) {
+               /* GW selection class is not available if the routing algorithm
+                * in use does not implement the GW API
+                */
+               if (nla_put_u32(msg, BATADV_ATTR_GW_SEL_CLASS,
+                               atomic_read(&bat_priv->gw.sel_class)))
+                       goto nla_put_failure;
+       }
+
+       if (nla_put_u8(msg, BATADV_ATTR_HOP_PENALTY,
+                      atomic_read(&bat_priv->hop_penalty)))
+               goto nla_put_failure;
+
+#ifdef CONFIG_BATMAN_ADV_DEBUG
+       if (nla_put_u32(msg, BATADV_ATTR_LOG_LEVEL,
+                       atomic_read(&bat_priv->log_level)))
+               goto nla_put_failure;
+#endif /* CONFIG_BATMAN_ADV_DEBUG */
+
+#ifdef CONFIG_BATMAN_ADV_MCAST
+       if (nla_put_u8(msg, BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED,
+                      !atomic_read(&bat_priv->multicast_mode)))
+               goto nla_put_failure;
+#endif /* CONFIG_BATMAN_ADV_MCAST */
+
+#ifdef CONFIG_BATMAN_ADV_NC
+       if (nla_put_u8(msg, BATADV_ATTR_NETWORK_CODING_ENABLED,
+                      !!atomic_read(&bat_priv->network_coding)))
+               goto nla_put_failure;
+#endif /* CONFIG_BATMAN_ADV_NC */
+
+       if (nla_put_u32(msg, BATADV_ATTR_ORIG_INTERVAL,
+                       atomic_read(&bat_priv->orig_interval)))
+               goto nla_put_failure;
 
- out:
        if (primary_if)
                batadv_hardif_put(primary_if);
 
-       return ret;
+       genlmsg_end(msg, hdr);
+       return 0;
+
+nla_put_failure:
+       if (primary_if)
+               batadv_hardif_put(primary_if);
+
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
 }
 
 /**
- * batadv_netlink_get_mesh_info() - handle incoming BATADV_CMD_GET_MESH_INFO
- *  netlink request
- * @skb: received netlink message
- * @info: receiver information
+ * batadv_netlink_notify_mesh() - send softif attributes to listener
+ * @bat_priv: the bat priv with all the soft interface information
  *
  * Return: 0 on success, < 0 on error
  */
-static int
-batadv_netlink_get_mesh_info(struct sk_buff *skb, struct genl_info *info)
+int batadv_netlink_notify_mesh(struct batadv_priv *bat_priv)
 {
-       struct net *net = genl_info_net(info);
-       struct net_device *soft_iface;
-       struct sk_buff *msg = NULL;
-       void *msg_head;
-       int ifindex;
+       struct sk_buff *msg;
        int ret;
 
-       if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
-               return -EINVAL;
-
-       ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
-       if (!ifindex)
-               return -EINVAL;
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
 
-       soft_iface = dev_get_by_index(net, ifindex);
-       if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
-               ret = -ENODEV;
-               goto out;
+       ret = batadv_netlink_mesh_fill(msg, bat_priv, BATADV_CMD_SET_MESH,
+                                      0, 0, 0);
+       if (ret < 0) {
+               nlmsg_free(msg);
+               return ret;
        }
 
+       genlmsg_multicast_netns(&batadv_netlink_family,
+                               dev_net(bat_priv->soft_iface), msg, 0,
+                               BATADV_NL_MCGRP_CONFIG, GFP_KERNEL);
+
+       return 0;
+}
+
+/**
+ * batadv_netlink_get_mesh() - Get softif attributes
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_netlink_get_mesh(struct sk_buff *skb, struct genl_info *info)
+{
+       struct batadv_priv *bat_priv = info->user_ptr[0];
+       struct sk_buff *msg;
+       int ret;
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg) {
-               ret = -ENOMEM;
-               goto out;
+       if (!msg)
+               return -ENOMEM;
+
+       ret = batadv_netlink_mesh_fill(msg, bat_priv, BATADV_CMD_GET_MESH,
+                                      info->snd_portid, info->snd_seq, 0);
+       if (ret < 0) {
+               nlmsg_free(msg);
+               return ret;
        }
 
-       msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
-                              &batadv_netlink_family, 0,
-                              BATADV_CMD_GET_MESH_INFO);
-       if (!msg_head) {
-               ret = -ENOBUFS;
-               goto out;
+       ret = genlmsg_reply(msg, info);
+
+       return ret;
+}
+
+/**
+ * batadv_netlink_set_mesh() - Set softif attributes
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info)
+{
+       struct batadv_priv *bat_priv = info->user_ptr[0];
+       struct nlattr *attr;
+
+       if (info->attrs[BATADV_ATTR_AGGREGATED_OGMS_ENABLED]) {
+               attr = info->attrs[BATADV_ATTR_AGGREGATED_OGMS_ENABLED];
+
+               atomic_set(&bat_priv->aggregated_ogms, !!nla_get_u8(attr));
        }
 
-       ret = batadv_netlink_mesh_info_put(msg, soft_iface);
+       if (info->attrs[BATADV_ATTR_AP_ISOLATION_ENABLED]) {
+               attr = info->attrs[BATADV_ATTR_AP_ISOLATION_ENABLED];
 
- out:
-       if (soft_iface)
-               dev_put(soft_iface);
+               batadv_netlink_set_mesh_ap_isolation(attr, bat_priv);
+       }
 
-       if (ret) {
-               if (msg)
-                       nlmsg_free(msg);
-               return ret;
+       if (info->attrs[BATADV_ATTR_ISOLATION_MARK]) {
+               attr = info->attrs[BATADV_ATTR_ISOLATION_MARK];
+
+               bat_priv->isolation_mark = nla_get_u32(attr);
        }
 
-       genlmsg_end(msg, msg_head);
-       return genlmsg_reply(msg, info);
+       if (info->attrs[BATADV_ATTR_ISOLATION_MASK]) {
+               attr = info->attrs[BATADV_ATTR_ISOLATION_MASK];
+
+               bat_priv->isolation_mark_mask = nla_get_u32(attr);
+       }
+
+       if (info->attrs[BATADV_ATTR_BONDING_ENABLED]) {
+               attr = info->attrs[BATADV_ATTR_BONDING_ENABLED];
+
+               atomic_set(&bat_priv->bonding, !!nla_get_u8(attr));
+       }
+
+#ifdef CONFIG_BATMAN_ADV_BLA
+       if (info->attrs[BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED]) {
+               attr = info->attrs[BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED];
+
+               atomic_set(&bat_priv->bridge_loop_avoidance,
+                          !!nla_get_u8(attr));
+               batadv_bla_status_update(bat_priv->soft_iface);
+       }
+#endif /* CONFIG_BATMAN_ADV_BLA */
+
+#ifdef CONFIG_BATMAN_ADV_DAT
+       if (info->attrs[BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED]) {
+               attr = info->attrs[BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED];
+
+               atomic_set(&bat_priv->distributed_arp_table,
+                          !!nla_get_u8(attr));
+               batadv_dat_status_update(bat_priv->soft_iface);
+       }
+#endif /* CONFIG_BATMAN_ADV_DAT */
+
+       if (info->attrs[BATADV_ATTR_FRAGMENTATION_ENABLED]) {
+               attr = info->attrs[BATADV_ATTR_FRAGMENTATION_ENABLED];
+
+               atomic_set(&bat_priv->fragmentation, !!nla_get_u8(attr));
+               batadv_update_min_mtu(bat_priv->soft_iface);
+       }
+
+       if (info->attrs[BATADV_ATTR_GW_BANDWIDTH_DOWN]) {
+               attr = info->attrs[BATADV_ATTR_GW_BANDWIDTH_DOWN];
+
+               atomic_set(&bat_priv->gw.bandwidth_down, nla_get_u32(attr));
+               batadv_gw_tvlv_container_update(bat_priv);
+       }
+
+       if (info->attrs[BATADV_ATTR_GW_BANDWIDTH_UP]) {
+               attr = info->attrs[BATADV_ATTR_GW_BANDWIDTH_UP];
+
+               atomic_set(&bat_priv->gw.bandwidth_up, nla_get_u32(attr));
+               batadv_gw_tvlv_container_update(bat_priv);
+       }
+
+       if (info->attrs[BATADV_ATTR_GW_MODE]) {
+               u8 gw_mode;
+
+               attr = info->attrs[BATADV_ATTR_GW_MODE];
+               gw_mode = nla_get_u8(attr);
+
+               if (gw_mode <= BATADV_GW_MODE_SERVER) {
+                       /* Invoking batadv_gw_reselect() is not enough to really
+                        * de-select the current GW. It will only instruct the
+                        * gateway client code to perform a re-election the next
+                        * time that this is needed.
+                        *
+                        * When gw client mode is being switched off the current
+                        * GW must be de-selected explicitly otherwise no GW_ADD
+                        * uevent is thrown on client mode re-activation. This
+                        * is operation is performed in
+                        * batadv_gw_check_client_stop().
+                        */
+                       batadv_gw_reselect(bat_priv);
+
+                       /* always call batadv_gw_check_client_stop() before
+                        * changing the gateway state
+                        */
+                       batadv_gw_check_client_stop(bat_priv);
+                       atomic_set(&bat_priv->gw.mode, gw_mode);
+                       batadv_gw_tvlv_container_update(bat_priv);
+               }
+       }
+
+       if (info->attrs[BATADV_ATTR_GW_SEL_CLASS] &&
+           bat_priv->algo_ops->gw.get_best_gw_node &&
+           bat_priv->algo_ops->gw.is_eligible) {
+               /* setting the GW selection class is allowed only if the routing
+                * algorithm in use implements the GW API
+                */
+
+               u32 sel_class_max = 0xffffffffu;
+               u32 sel_class;
+
+               attr = info->attrs[BATADV_ATTR_GW_SEL_CLASS];
+               sel_class = nla_get_u32(attr);
+
+               if (!bat_priv->algo_ops->gw.store_sel_class)
+                       sel_class_max = BATADV_TQ_MAX_VALUE;
+
+               if (sel_class >= 1 && sel_class <= sel_class_max) {
+                       atomic_set(&bat_priv->gw.sel_class, sel_class);
+                       batadv_gw_reselect(bat_priv);
+               }
+       }
+
+       if (info->attrs[BATADV_ATTR_HOP_PENALTY]) {
+               attr = info->attrs[BATADV_ATTR_HOP_PENALTY];
+
+               atomic_set(&bat_priv->hop_penalty, nla_get_u8(attr));
+       }
+
+#ifdef CONFIG_BATMAN_ADV_DEBUG
+       if (info->attrs[BATADV_ATTR_LOG_LEVEL]) {
+               attr = info->attrs[BATADV_ATTR_LOG_LEVEL];
+
+               atomic_set(&bat_priv->log_level,
+                          nla_get_u32(attr) & BATADV_DBG_ALL);
+       }
+#endif /* CONFIG_BATMAN_ADV_DEBUG */
+
+#ifdef CONFIG_BATMAN_ADV_MCAST
+       if (info->attrs[BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED]) {
+               attr = info->attrs[BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED];
+
+               atomic_set(&bat_priv->multicast_mode, !nla_get_u8(attr));
+       }
+#endif /* CONFIG_BATMAN_ADV_MCAST */
+
+#ifdef CONFIG_BATMAN_ADV_NC
+       if (info->attrs[BATADV_ATTR_NETWORK_CODING_ENABLED]) {
+               attr = info->attrs[BATADV_ATTR_NETWORK_CODING_ENABLED];
+
+               atomic_set(&bat_priv->network_coding, !!nla_get_u8(attr));
+               batadv_nc_status_update(bat_priv->soft_iface);
+       }
+#endif /* CONFIG_BATMAN_ADV_NC */
+
+       if (info->attrs[BATADV_ATTR_ORIG_INTERVAL]) {
+               u32 orig_interval;
+
+               attr = info->attrs[BATADV_ATTR_ORIG_INTERVAL];
+               orig_interval = nla_get_u32(attr);
+
+               orig_interval = min_t(u32, orig_interval, INT_MAX);
+               orig_interval = max_t(u32, orig_interval, 2 * BATADV_JITTER);
+
+               atomic_set(&bat_priv->orig_interval, orig_interval);
+       }
+
+       batadv_netlink_notify_mesh(bat_priv);
+
+       return 0;
 }
 
 /**
@@ -329,40 +709,24 @@ err_genlmsg:
 static int
 batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info)
 {
-       struct net *net = genl_info_net(info);
-       struct net_device *soft_iface;
-       struct batadv_priv *bat_priv;
+       struct batadv_priv *bat_priv = info->user_ptr[0];
        struct sk_buff *msg = NULL;
        u32 test_length;
        void *msg_head;
-       int ifindex;
        u32 cookie;
        u8 *dst;
        int ret;
 
-       if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
-               return -EINVAL;
-
        if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
                return -EINVAL;
 
        if (!info->attrs[BATADV_ATTR_TPMETER_TEST_TIME])
                return -EINVAL;
 
-       ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
-       if (!ifindex)
-               return -EINVAL;
-
        dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
 
        test_length = nla_get_u32(info->attrs[BATADV_ATTR_TPMETER_TEST_TIME]);
 
-       soft_iface = dev_get_by_index(net, ifindex);
-       if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
-               ret = -ENODEV;
-               goto out;
-       }
-
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg) {
                ret = -ENOMEM;
@@ -377,15 +741,11 @@ batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       bat_priv = netdev_priv(soft_iface);
        batadv_tp_start(bat_priv, dst, test_length, &cookie);
 
        ret = batadv_netlink_tp_meter_put(msg, cookie);
 
  out:
-       if (soft_iface)
-               dev_put(soft_iface);
-
        if (ret) {
                if (msg)
                        nlmsg_free(msg);
@@ -406,65 +766,53 @@ batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info)
 static int
 batadv_netlink_tp_meter_cancel(struct sk_buff *skb, struct genl_info *info)
 {
-       struct net *net = genl_info_net(info);
-       struct net_device *soft_iface;
-       struct batadv_priv *bat_priv;
-       int ifindex;
+       struct batadv_priv *bat_priv = info->user_ptr[0];
        u8 *dst;
        int ret = 0;
 
-       if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
-               return -EINVAL;
-
        if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
                return -EINVAL;
 
-       ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
-       if (!ifindex)
-               return -EINVAL;
-
        dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
 
-       soft_iface = dev_get_by_index(net, ifindex);
-       if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
-               ret = -ENODEV;
-               goto out;
-       }
-
-       bat_priv = netdev_priv(soft_iface);
        batadv_tp_stop(bat_priv, dst, BATADV_TP_REASON_CANCEL);
 
-out:
-       if (soft_iface)
-               dev_put(soft_iface);
-
        return ret;
 }
 
 /**
- * batadv_netlink_dump_hardif_entry() - Dump one hard interface into a message
+ * batadv_netlink_hardif_fill() - Fill message with hardif attributes
  * @msg: Netlink message to dump into
+ * @bat_priv: the bat priv with all the soft interface information
+ * @hard_iface: hard interface which was modified
+ * @cmd: type of message to generate
  * @portid: Port making netlink request
+ * @seq: sequence number for message
+ * @flags: Additional flags for message
  * @cb: Control block containing additional options
- * @hard_iface: Hard interface to dump
  *
- * Return: error code, or 0 on success
+ * Return: 0 on success or negative error number in case of failure
  */
-static int
-batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid,
-                                struct netlink_callback *cb,
-                                struct batadv_hard_iface *hard_iface)
+static int batadv_netlink_hardif_fill(struct sk_buff *msg,
+                                     struct batadv_priv *bat_priv,
+                                     struct batadv_hard_iface *hard_iface,
+                                     enum batadv_nl_commands cmd,
+                                     u32 portid, u32 seq, int flags,
+                                     struct netlink_callback *cb)
 {
        struct net_device *net_dev = hard_iface->net_dev;
        void *hdr;
 
-       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
-                         &batadv_netlink_family, NLM_F_MULTI,
-                         BATADV_CMD_GET_HARDIFS);
+       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, flags, cmd);
        if (!hdr)
-               return -EMSGSIZE;
+               return -ENOBUFS;
 
-       genl_dump_check_consistent(cb, hdr);
+       if (cb)
+               genl_dump_check_consistent(cb, hdr);
+
+       if (nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX,
+                       bat_priv->soft_iface->ifindex))
+               goto nla_put_failure;
 
        if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
                        net_dev->ifindex) ||
@@ -479,27 +827,137 @@ batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid,
                        goto nla_put_failure;
        }
 
+#ifdef CONFIG_BATMAN_ADV_BATMAN_V
+       if (nla_put_u32(msg, BATADV_ATTR_ELP_INTERVAL,
+                       atomic_read(&hard_iface->bat_v.elp_interval)))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, BATADV_ATTR_THROUGHPUT_OVERRIDE,
+                       atomic_read(&hard_iface->bat_v.throughput_override)))
+               goto nla_put_failure;
+#endif /* CONFIG_BATMAN_ADV_BATMAN_V */
+
        genlmsg_end(msg, hdr);
        return 0;
 
- nla_put_failure:
+nla_put_failure:
        genlmsg_cancel(msg, hdr);
        return -EMSGSIZE;
 }
 
 /**
- * batadv_netlink_dump_hardifs() - Dump all hard interface into a messages
+ * batadv_netlink_notify_hardif() - send hardif attributes to listener
+ * @bat_priv: the bat priv with all the soft interface information
+ * @hard_iface: hard interface which was modified
+ *
+ * Return: 0 on success, < 0 on error
+ */
+int batadv_netlink_notify_hardif(struct batadv_priv *bat_priv,
+                                struct batadv_hard_iface *hard_iface)
+{
+       struct sk_buff *msg;
+       int ret;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       ret = batadv_netlink_hardif_fill(msg, bat_priv, hard_iface,
+                                        BATADV_CMD_SET_HARDIF, 0, 0, 0, NULL);
+       if (ret < 0) {
+               nlmsg_free(msg);
+               return ret;
+       }
+
+       genlmsg_multicast_netns(&batadv_netlink_family,
+                               dev_net(bat_priv->soft_iface), msg, 0,
+                               BATADV_NL_MCGRP_CONFIG, GFP_KERNEL);
+
+       return 0;
+}
+
+/**
+ * batadv_netlink_get_hardif() - Get hardif attributes
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_netlink_get_hardif(struct sk_buff *skb,
+                                    struct genl_info *info)
+{
+       struct batadv_hard_iface *hard_iface = info->user_ptr[1];
+       struct batadv_priv *bat_priv = info->user_ptr[0];
+       struct sk_buff *msg;
+       int ret;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       ret = batadv_netlink_hardif_fill(msg, bat_priv, hard_iface,
+                                        BATADV_CMD_GET_HARDIF,
+                                        info->snd_portid, info->snd_seq, 0,
+                                        NULL);
+       if (ret < 0) {
+               nlmsg_free(msg);
+               return ret;
+       }
+
+       ret = genlmsg_reply(msg, info);
+
+       return ret;
+}
+
+/**
+ * batadv_netlink_set_hardif() - Set hardif attributes
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_netlink_set_hardif(struct sk_buff *skb,
+                                    struct genl_info *info)
+{
+       struct batadv_hard_iface *hard_iface = info->user_ptr[1];
+       struct batadv_priv *bat_priv = info->user_ptr[0];
+
+#ifdef CONFIG_BATMAN_ADV_BATMAN_V
+       struct nlattr *attr;
+
+       if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) {
+               attr = info->attrs[BATADV_ATTR_ELP_INTERVAL];
+
+               atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr));
+       }
+
+       if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) {
+               attr = info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE];
+
+               atomic_set(&hard_iface->bat_v.throughput_override,
+                          nla_get_u32(attr));
+       }
+#endif /* CONFIG_BATMAN_ADV_BATMAN_V */
+
+       batadv_netlink_notify_hardif(bat_priv, hard_iface);
+
+       return 0;
+}
+
+/**
+ * batadv_netlink_dump_hardif() - Dump all hard interface into a messages
  * @msg: Netlink message to dump into
  * @cb: Parameters from query
  *
  * Return: error code, or length of reply message on success
  */
 static int
-batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
+batadv_netlink_dump_hardif(struct sk_buff *msg, struct netlink_callback *cb)
 {
        struct net *net = sock_net(cb->skb->sk);
        struct net_device *soft_iface;
        struct batadv_hard_iface *hard_iface;
+       struct batadv_priv *bat_priv;
        int ifindex;
        int portid = NETLINK_CB(cb->skb).portid;
        int skip = cb->args[0];
@@ -519,6 +977,8 @@ batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
                return -ENODEV;
        }
 
+       bat_priv = netdev_priv(soft_iface);
+
        rtnl_lock();
        cb->seq = batadv_hardif_generation << 1 | 1;
 
@@ -529,8 +989,10 @@ batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
                if (i++ < skip)
                        continue;
 
-               if (batadv_netlink_dump_hardif_entry(msg, portid, cb,
-                                                    hard_iface)) {
+               if (batadv_netlink_hardif_fill(msg, bat_priv, hard_iface,
+                                              BATADV_CMD_GET_HARDIF,
+                                              portid, cb->nlh->nlmsg_seq,
+                                              NLM_F_MULTI, cb)) {
                        i--;
                        break;
                }
@@ -545,24 +1007,361 @@ batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
        return msg->len;
 }
 
+/**
+ * batadv_netlink_vlan_fill() - Fill message with vlan attributes
+ * @msg: Netlink message to dump into
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vlan: vlan which was modified
+ * @cmd: type of message to generate
+ * @portid: Port making netlink request
+ * @seq: sequence number for message
+ * @flags: Additional flags for message
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_netlink_vlan_fill(struct sk_buff *msg,
+                                   struct batadv_priv *bat_priv,
+                                   struct batadv_softif_vlan *vlan,
+                                   enum batadv_nl_commands cmd,
+                                   u32 portid, u32 seq, int flags)
+{
+       void *hdr;
+
+       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, flags, cmd);
+       if (!hdr)
+               return -ENOBUFS;
+
+       if (nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX,
+                       bat_priv->soft_iface->ifindex))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, BATADV_ATTR_VLANID, vlan->vid & VLAN_VID_MASK))
+               goto nla_put_failure;
+
+       if (nla_put_u8(msg, BATADV_ATTR_AP_ISOLATION_ENABLED,
+                      !!atomic_read(&vlan->ap_isolation)))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+/**
+ * batadv_netlink_notify_vlan() - send vlan attributes to listener
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vlan: vlan which was modified
+ *
+ * Return: 0 on success, < 0 on error
+ */
+int batadv_netlink_notify_vlan(struct batadv_priv *bat_priv,
+                              struct batadv_softif_vlan *vlan)
+{
+       struct sk_buff *msg;
+       int ret;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       ret = batadv_netlink_vlan_fill(msg, bat_priv, vlan,
+                                      BATADV_CMD_SET_VLAN, 0, 0, 0);
+       if (ret < 0) {
+               nlmsg_free(msg);
+               return ret;
+       }
+
+       genlmsg_multicast_netns(&batadv_netlink_family,
+                               dev_net(bat_priv->soft_iface), msg, 0,
+                               BATADV_NL_MCGRP_CONFIG, GFP_KERNEL);
+
+       return 0;
+}
+
+/**
+ * batadv_netlink_get_vlan() - Get vlan attributes
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_netlink_get_vlan(struct sk_buff *skb, struct genl_info *info)
+{
+       struct batadv_softif_vlan *vlan = info->user_ptr[1];
+       struct batadv_priv *bat_priv = info->user_ptr[0];
+       struct sk_buff *msg;
+       int ret;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       ret = batadv_netlink_vlan_fill(msg, bat_priv, vlan, BATADV_CMD_GET_VLAN,
+                                      info->snd_portid, info->snd_seq, 0);
+       if (ret < 0) {
+               nlmsg_free(msg);
+               return ret;
+       }
+
+       ret = genlmsg_reply(msg, info);
+
+       return ret;
+}
+
+/**
+ * batadv_netlink_set_vlan() - Get vlan attributes
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_netlink_set_vlan(struct sk_buff *skb, struct genl_info *info)
+{
+       struct batadv_softif_vlan *vlan = info->user_ptr[1];
+       struct batadv_priv *bat_priv = info->user_ptr[0];
+       struct nlattr *attr;
+
+       if (info->attrs[BATADV_ATTR_AP_ISOLATION_ENABLED]) {
+               attr = info->attrs[BATADV_ATTR_AP_ISOLATION_ENABLED];
+
+               atomic_set(&vlan->ap_isolation, !!nla_get_u8(attr));
+       }
+
+       batadv_netlink_notify_vlan(bat_priv, vlan);
+
+       return 0;
+}
+
+/**
+ * batadv_get_softif_from_info() - Retrieve soft interface from genl attributes
+ * @net: the applicable net namespace
+ * @info: receiver information
+ *
+ * Return: Pointer to soft interface (with increased refcnt) on success, error
+ *  pointer on error
+ */
+static struct net_device *
+batadv_get_softif_from_info(struct net *net, struct genl_info *info)
+{
+       struct net_device *soft_iface;
+       int ifindex;
+
+       if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
+               return ERR_PTR(-EINVAL);
+
+       ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
+
+       soft_iface = dev_get_by_index(net, ifindex);
+       if (!soft_iface)
+               return ERR_PTR(-ENODEV);
+
+       if (!batadv_softif_is_valid(soft_iface))
+               goto err_put_softif;
+
+       return soft_iface;
+
+err_put_softif:
+       dev_put(soft_iface);
+
+       return ERR_PTR(-EINVAL);
+}
+
+/**
+ * batadv_get_hardif_from_info() - Retrieve hardif from genl attributes
+ * @bat_priv: the bat priv with all the soft interface information
+ * @net: the applicable net namespace
+ * @info: receiver information
+ *
+ * Return: Pointer to hard interface (with increased refcnt) on success, error
+ *  pointer on error
+ */
+static struct batadv_hard_iface *
+batadv_get_hardif_from_info(struct batadv_priv *bat_priv, struct net *net,
+                           struct genl_info *info)
+{
+       struct batadv_hard_iface *hard_iface;
+       struct net_device *hard_dev;
+       unsigned int hardif_index;
+
+       if (!info->attrs[BATADV_ATTR_HARD_IFINDEX])
+               return ERR_PTR(-EINVAL);
+
+       hardif_index = nla_get_u32(info->attrs[BATADV_ATTR_HARD_IFINDEX]);
+
+       hard_dev = dev_get_by_index(net, hardif_index);
+       if (!hard_dev)
+               return ERR_PTR(-ENODEV);
+
+       hard_iface = batadv_hardif_get_by_netdev(hard_dev);
+       if (!hard_iface)
+               goto err_put_harddev;
+
+       if (hard_iface->soft_iface != bat_priv->soft_iface)
+               goto err_put_hardif;
+
+       /* hard_dev is referenced by hard_iface and not needed here */
+       dev_put(hard_dev);
+
+       return hard_iface;
+
+err_put_hardif:
+       batadv_hardif_put(hard_iface);
+err_put_harddev:
+       dev_put(hard_dev);
+
+       return ERR_PTR(-EINVAL);
+}
+
+/**
+ * batadv_get_vlan_from_info() - Retrieve vlan from genl attributes
+ * @bat_priv: the bat priv with all the soft interface information
+ * @net: the applicable net namespace
+ * @info: receiver information
+ *
+ * Return: Pointer to vlan on success (with increased refcnt), error pointer
+ *  on error
+ */
+static struct batadv_softif_vlan *
+batadv_get_vlan_from_info(struct batadv_priv *bat_priv, struct net *net,
+                         struct genl_info *info)
+{
+       struct batadv_softif_vlan *vlan;
+       u16 vid;
+
+       if (!info->attrs[BATADV_ATTR_VLANID])
+               return ERR_PTR(-EINVAL);
+
+       vid = nla_get_u16(info->attrs[BATADV_ATTR_VLANID]);
+
+       vlan = batadv_softif_vlan_get(bat_priv, vid | BATADV_VLAN_HAS_TAG);
+       if (!vlan)
+               return ERR_PTR(-ENOENT);
+
+       return vlan;
+}
+
+/**
+ * batadv_pre_doit() - Prepare batman-adv genl doit request
+ * @ops: requested netlink operation
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+static int batadv_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+                          struct genl_info *info)
+{
+       struct net *net = genl_info_net(info);
+       struct batadv_hard_iface *hard_iface;
+       struct batadv_priv *bat_priv = NULL;
+       struct batadv_softif_vlan *vlan;
+       struct net_device *soft_iface;
+       u8 user_ptr1_flags;
+       u8 mesh_dep_flags;
+       int ret;
+
+       user_ptr1_flags = BATADV_FLAG_NEED_HARDIF | BATADV_FLAG_NEED_VLAN;
+       if (WARN_ON(hweight8(ops->internal_flags & user_ptr1_flags) > 1))
+               return -EINVAL;
+
+       mesh_dep_flags = BATADV_FLAG_NEED_HARDIF | BATADV_FLAG_NEED_VLAN;
+       if (WARN_ON((ops->internal_flags & mesh_dep_flags) &&
+                   (~ops->internal_flags & BATADV_FLAG_NEED_MESH)))
+               return -EINVAL;
+
+       if (ops->internal_flags & BATADV_FLAG_NEED_MESH) {
+               soft_iface = batadv_get_softif_from_info(net, info);
+               if (IS_ERR(soft_iface))
+                       return PTR_ERR(soft_iface);
+
+               bat_priv = netdev_priv(soft_iface);
+               info->user_ptr[0] = bat_priv;
+       }
+
+       if (ops->internal_flags & BATADV_FLAG_NEED_HARDIF) {
+               hard_iface = batadv_get_hardif_from_info(bat_priv, net, info);
+               if (IS_ERR(hard_iface)) {
+                       ret = PTR_ERR(hard_iface);
+                       goto err_put_softif;
+               }
+
+               info->user_ptr[1] = hard_iface;
+       }
+
+       if (ops->internal_flags & BATADV_FLAG_NEED_VLAN) {
+               vlan = batadv_get_vlan_from_info(bat_priv, net, info);
+               if (IS_ERR(vlan)) {
+                       ret = PTR_ERR(vlan);
+                       goto err_put_softif;
+               }
+
+               info->user_ptr[1] = vlan;
+       }
+
+       return 0;
+
+err_put_softif:
+       if (bat_priv)
+               dev_put(bat_priv->soft_iface);
+
+       return ret;
+}
+
+/**
+ * batadv_post_doit() - End batman-adv genl doit request
+ * @ops: requested netlink operation
+ * @skb: Netlink message with request data
+ * @info: receiver information
+ */
+static void batadv_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+                            struct genl_info *info)
+{
+       struct batadv_hard_iface *hard_iface;
+       struct batadv_softif_vlan *vlan;
+       struct batadv_priv *bat_priv;
+
+       if (ops->internal_flags & BATADV_FLAG_NEED_HARDIF &&
+           info->user_ptr[1]) {
+               hard_iface = info->user_ptr[1];
+
+               batadv_hardif_put(hard_iface);
+       }
+
+       if (ops->internal_flags & BATADV_FLAG_NEED_VLAN && info->user_ptr[1]) {
+               vlan = info->user_ptr[1];
+               batadv_softif_vlan_put(vlan);
+       }
+
+       if (ops->internal_flags & BATADV_FLAG_NEED_MESH && info->user_ptr[0]) {
+               bat_priv = info->user_ptr[0];
+               dev_put(bat_priv->soft_iface);
+       }
+}
+
 static const struct genl_ops batadv_netlink_ops[] = {
        {
-               .cmd = BATADV_CMD_GET_MESH_INFO,
-               .flags = GENL_ADMIN_PERM,
+               .cmd = BATADV_CMD_GET_MESH,
+               /* can be retrieved by unprivileged users */
                .policy = batadv_netlink_policy,
-               .doit = batadv_netlink_get_mesh_info,
+               .doit = batadv_netlink_get_mesh,
+               .internal_flags = BATADV_FLAG_NEED_MESH,
        },
        {
                .cmd = BATADV_CMD_TP_METER,
                .flags = GENL_ADMIN_PERM,
                .policy = batadv_netlink_policy,
                .doit = batadv_netlink_tp_meter_start,
+               .internal_flags = BATADV_FLAG_NEED_MESH,
        },
        {
                .cmd = BATADV_CMD_TP_METER_CANCEL,
                .flags = GENL_ADMIN_PERM,
                .policy = batadv_netlink_policy,
                .doit = batadv_netlink_tp_meter_cancel,
+               .internal_flags = BATADV_FLAG_NEED_MESH,
        },
        {
                .cmd = BATADV_CMD_GET_ROUTING_ALGOS,
@@ -571,10 +1370,13 @@ static const struct genl_ops batadv_netlink_ops[] = {
                .dumpit = batadv_algo_dump,
        },
        {
-               .cmd = BATADV_CMD_GET_HARDIFS,
-               .flags = GENL_ADMIN_PERM,
+               .cmd = BATADV_CMD_GET_HARDIF,
+               /* can be retrieved by unprivileged users */
                .policy = batadv_netlink_policy,
-               .dumpit = batadv_netlink_dump_hardifs,
+               .dumpit = batadv_netlink_dump_hardif,
+               .doit = batadv_netlink_get_hardif,
+               .internal_flags = BATADV_FLAG_NEED_MESH |
+                                 BATADV_FLAG_NEED_HARDIF,
        },
        {
                .cmd = BATADV_CMD_GET_TRANSTABLE_LOCAL,
@@ -630,7 +1432,37 @@ static const struct genl_ops batadv_netlink_ops[] = {
                .policy = batadv_netlink_policy,
                .dumpit = batadv_mcast_flags_dump,
        },
-
+       {
+               .cmd = BATADV_CMD_SET_MESH,
+               .flags = GENL_ADMIN_PERM,
+               .policy = batadv_netlink_policy,
+               .doit = batadv_netlink_set_mesh,
+               .internal_flags = BATADV_FLAG_NEED_MESH,
+       },
+       {
+               .cmd = BATADV_CMD_SET_HARDIF,
+               .flags = GENL_ADMIN_PERM,
+               .policy = batadv_netlink_policy,
+               .doit = batadv_netlink_set_hardif,
+               .internal_flags = BATADV_FLAG_NEED_MESH |
+                                 BATADV_FLAG_NEED_HARDIF,
+       },
+       {
+               .cmd = BATADV_CMD_GET_VLAN,
+               /* can be retrieved by unprivileged users */
+               .policy = batadv_netlink_policy,
+               .doit = batadv_netlink_get_vlan,
+               .internal_flags = BATADV_FLAG_NEED_MESH |
+                                 BATADV_FLAG_NEED_VLAN,
+       },
+       {
+               .cmd = BATADV_CMD_SET_VLAN,
+               .flags = GENL_ADMIN_PERM,
+               .policy = batadv_netlink_policy,
+               .doit = batadv_netlink_set_vlan,
+               .internal_flags = BATADV_FLAG_NEED_MESH |
+                                 BATADV_FLAG_NEED_VLAN,
+       },
 };
 
 struct genl_family batadv_netlink_family __ro_after_init = {
@@ -639,6 +1471,8 @@ struct genl_family batadv_netlink_family __ro_after_init = {
        .version = 1,
        .maxattr = BATADV_ATTR_MAX,
        .netnsok = true,
+       .pre_doit = batadv_pre_doit,
+       .post_doit = batadv_post_doit,
        .module = THIS_MODULE,
        .ops = batadv_netlink_ops,
        .n_ops = ARRAY_SIZE(batadv_netlink_ops),
index 571d9a5..7273368 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2016-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2016-2019  B.A.T.M.A.N. contributors:
  *
  * Matthias Schiffer
  *
@@ -34,6 +34,12 @@ int batadv_netlink_tpmeter_notify(struct batadv_priv *bat_priv, const u8 *dst,
                                  u8 result, u32 test_time, u64 total_bytes,
                                  u32 cookie);
 
+int batadv_netlink_notify_mesh(struct batadv_priv *bat_priv);
+int batadv_netlink_notify_hardif(struct batadv_priv *bat_priv,
+                                struct batadv_hard_iface *hard_iface);
+int batadv_netlink_notify_vlan(struct batadv_priv *bat_priv,
+                              struct batadv_softif_vlan *vlan);
+
 extern struct genl_family batadv_netlink_family;
 
 #endif /* _NET_BATMAN_ADV_NETLINK_H_ */
index 34caf12..278762b 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2012-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2012-2019  B.A.T.M.A.N. contributors:
  *
  * Martin Hundebøll, Jeppe Ledet-Pedersen
  *
index 65c3468..96ef0a5 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2012-2019  B.A.T.M.A.N. contributors:
  *
  * Martin Hundebøll, Jeppe Ledet-Pedersen
  *
index 56a981a..e5cdf89 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2009-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2009-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index a8b4c7b..dca1e4a 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index cc3ed93..cae0e5d 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
@@ -1043,6 +1043,8 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
                                                        hdr_size))
                        goto rx_success;
 
+               batadv_dat_snoop_incoming_dhcp_ack(bat_priv, skb, hdr_size);
+
                batadv_interface_rx(recv_if->soft_iface, skb, hdr_size,
                                    orig_node);
 
@@ -1278,6 +1280,8 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
        if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb, hdr_size))
                goto rx_success;
 
+       batadv_dat_snoop_incoming_dhcp_ack(bat_priv, skb, hdr_size);
+
        /* broadcast for me */
        batadv_interface_rx(recv_if->soft_iface, skb, hdr_size, orig_node);
 
index db54c2d..0102d69 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index 4a35f5c..66a8b3e 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index 64cce07..1f61329 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index 5db5a0a..2e36723 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
 #include <linux/string.h>
 #include <linux/types.h>
 #include <uapi/linux/batadv_packet.h>
+#include <uapi/linux/batman_adv.h>
 
 #include "bat_algo.h"
 #include "bridge_loop_avoidance.h"
 #include "debugfs.h"
 #include "distributed-arp-table.h"
 #include "gateway_client.h"
-#include "gateway_common.h"
 #include "hard-interface.h"
 #include "multicast.h"
 #include "network-coding.h"
@@ -212,6 +212,7 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
        enum batadv_forw_mode forw_mode;
        struct batadv_orig_node *mcast_single_orig = NULL;
        int network_offset = ETH_HLEN;
+       __be16 proto;
 
        if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
                goto dropped;
@@ -221,14 +222,21 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
 
        netif_trans_update(soft_iface);
        vid = batadv_get_vid(skb, 0);
+
+       skb_reset_mac_header(skb);
        ethhdr = eth_hdr(skb);
 
-       switch (ntohs(ethhdr->h_proto)) {
+       proto = ethhdr->h_proto;
+
+       switch (ntohs(proto)) {
        case ETH_P_8021Q:
+               if (!pskb_may_pull(skb, sizeof(*vhdr)))
+                       goto dropped;
                vhdr = vlan_eth_hdr(skb);
+               proto = vhdr->h_vlan_encapsulated_proto;
 
                /* drop batman-in-batman packets to prevent loops */
-               if (vhdr->h_vlan_encapsulated_proto != htons(ETH_P_BATMAN)) {
+               if (proto != htons(ETH_P_BATMAN)) {
                        network_offset += VLAN_HLEN;
                        break;
                }
@@ -256,6 +264,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
                        goto dropped;
        }
 
+       /* Snoop address candidates from DHCPACKs for early DAT filling */
+       batadv_dat_snoop_outgoing_dhcp_ack(bat_priv, skb, proto, vid);
+
        /* don't accept stp packets. STP does not help in meshes.
         * better use the bridge loop avoidance ...
         *
index daf87f0..538bb66 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
index 09427fc..0b4b3fb 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2010-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2010-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
@@ -40,6 +40,7 @@
 #include <linux/stringify.h>
 #include <linux/workqueue.h>
 #include <uapi/linux/batadv_packet.h>
+#include <uapi/linux/batman_adv.h>
 
 #include "bridge_loop_avoidance.h"
 #include "distributed-arp-table.h"
@@ -47,6 +48,7 @@
 #include "gateway_common.h"
 #include "hard-interface.h"
 #include "log.h"
+#include "netlink.h"
 #include "network-coding.h"
 #include "soft-interface.h"
 
@@ -153,9 +155,14 @@ ssize_t batadv_store_##_name(struct kobject *kobj,                 \
 {                                                                      \
        struct net_device *net_dev = batadv_kobj_to_netdev(kobj);       \
        struct batadv_priv *bat_priv = netdev_priv(net_dev);            \
+       ssize_t length;                                                 \
+                                                                       \
+       length = __batadv_store_bool_attr(buff, count, _post_func, attr,\
+                                         &bat_priv->_name, net_dev);   \
                                                                        \
-       return __batadv_store_bool_attr(buff, count, _post_func, attr,  \
-                                       &bat_priv->_name, net_dev);     \
+       batadv_netlink_notify_mesh(bat_priv);                           \
+                                                                       \
+       return length;                                                  \
 }
 
 #define BATADV_ATTR_SIF_SHOW_BOOL(_name)                               \
@@ -185,11 +192,16 @@ ssize_t batadv_store_##_name(struct kobject *kobj,                        \
 {                                                                      \
        struct net_device *net_dev = batadv_kobj_to_netdev(kobj);       \
        struct batadv_priv *bat_priv = netdev_priv(net_dev);            \
+       ssize_t length;                                                 \
                                                                        \
-       return __batadv_store_uint_attr(buff, count, _min, _max,        \
-                                       _post_func, attr,               \
-                                       &bat_priv->_var, net_dev,       \
-                                       NULL);  \
+       length = __batadv_store_uint_attr(buff, count, _min, _max,      \
+                                         _post_func, attr,             \
+                                         &bat_priv->_var, net_dev,     \
+                                         NULL);                        \
+                                                                       \
+       batadv_netlink_notify_mesh(bat_priv);                           \
+                                                                       \
+       return length;                                                  \
 }
 
 #define BATADV_ATTR_SIF_SHOW_UINT(_name, _var)                         \
@@ -222,6 +234,11 @@ ssize_t batadv_store_vlan_##_name(struct kobject *kobj,                    \
                                              attr, &vlan->_name,       \
                                              bat_priv->soft_iface);    \
                                                                        \
+       if (vlan->vid)                                                  \
+               batadv_netlink_notify_vlan(bat_priv, vlan);             \
+       else                                                            \
+               batadv_netlink_notify_mesh(bat_priv);                   \
+                                                                       \
        batadv_softif_vlan_put(vlan);                                   \
        return res;                                                     \
 }
@@ -255,6 +272,7 @@ ssize_t batadv_store_##_name(struct kobject *kobj,                  \
 {                                                                      \
        struct net_device *net_dev = batadv_kobj_to_netdev(kobj);       \
        struct batadv_hard_iface *hard_iface;                           \
+       struct batadv_priv *bat_priv;                                   \
        ssize_t length;                                                 \
                                                                        \
        hard_iface = batadv_hardif_get_by_netdev(net_dev);              \
@@ -267,6 +285,11 @@ ssize_t batadv_store_##_name(struct kobject *kobj,                 \
                                          hard_iface->soft_iface,       \
                                          net_dev);                     \
                                                                        \
+       if (hard_iface->soft_iface) {                                   \
+               bat_priv = netdev_priv(hard_iface->soft_iface);         \
+               batadv_netlink_notify_hardif(bat_priv, hard_iface);     \
+       }                                                               \
+                                                                       \
        batadv_hardif_put(hard_iface);                          \
        return length;                                                  \
 }
@@ -536,6 +559,9 @@ static ssize_t batadv_store_gw_mode(struct kobject *kobj,
        batadv_gw_check_client_stop(bat_priv);
        atomic_set(&bat_priv->gw.mode, (unsigned int)gw_mode_tmp);
        batadv_gw_tvlv_container_update(bat_priv);
+
+       batadv_netlink_notify_mesh(bat_priv);
+
        return count;
 }
 
@@ -562,6 +588,7 @@ static ssize_t batadv_store_gw_sel_class(struct kobject *kobj,
                                         size_t count)
 {
        struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
+       ssize_t length;
 
        /* setting the GW selection class is allowed only if the routing
         * algorithm in use implements the GW API
@@ -577,10 +604,14 @@ static ssize_t batadv_store_gw_sel_class(struct kobject *kobj,
                return bat_priv->algo_ops->gw.store_sel_class(bat_priv, buff,
                                                              count);
 
-       return __batadv_store_uint_attr(buff, count, 1, BATADV_TQ_MAX_VALUE,
-                                       batadv_post_gw_reselect, attr,
-                                       &bat_priv->gw.sel_class,
-                                       bat_priv->soft_iface, NULL);
+       length = __batadv_store_uint_attr(buff, count, 1, BATADV_TQ_MAX_VALUE,
+                                         batadv_post_gw_reselect, attr,
+                                         &bat_priv->gw.sel_class,
+                                         bat_priv->soft_iface, NULL);
+
+       batadv_netlink_notify_mesh(bat_priv);
+
+       return length;
 }
 
 static ssize_t batadv_show_gw_bwidth(struct kobject *kobj,
@@ -600,12 +631,18 @@ static ssize_t batadv_store_gw_bwidth(struct kobject *kobj,
                                      struct attribute *attr, char *buff,
                                      size_t count)
 {
+       struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
        struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
+       ssize_t length;
 
        if (buff[count - 1] == '\n')
                buff[count - 1] = '\0';
 
-       return batadv_gw_bandwidth_set(net_dev, buff, count);
+       length = batadv_gw_bandwidth_set(net_dev, buff, count);
+
+       batadv_netlink_notify_mesh(bat_priv);
+
+       return length;
 }
 
 /**
@@ -673,6 +710,8 @@ static ssize_t batadv_store_isolation_mark(struct kobject *kobj,
                    "New skb mark for extended isolation: %#.8x/%#.8x\n",
                    bat_priv->isolation_mark, bat_priv->isolation_mark_mask);
 
+       batadv_netlink_notify_mesh(bat_priv);
+
        return count;
 }
 
@@ -1077,6 +1116,7 @@ static ssize_t batadv_store_throughput_override(struct kobject *kobj,
                                                struct attribute *attr,
                                                char *buff, size_t count)
 {
+       struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
        struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
        struct batadv_hard_iface *hard_iface;
        u32 tp_override;
@@ -1107,6 +1147,8 @@ static ssize_t batadv_store_throughput_override(struct kobject *kobj,
 
        atomic_set(&hard_iface->bat_v.throughput_override, tp_override);
 
+       batadv_netlink_notify_hardif(bat_priv, hard_iface);
+
 out:
        batadv_hardif_put(hard_iface);
        return count;
index c1e3fb6..705ffbe 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2010-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2010-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner
  *
index 11520de..500109b 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2012-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2012-2019  B.A.T.M.A.N. contributors:
  *
  * Edo Monticelli, Antonio Quartulli
  *
index 68e6009..6b4d0f7 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2012-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2012-2019  B.A.T.M.A.N. contributors:
  *
  * Edo Monticelli, Antonio Quartulli
  *
index 8e10242..f77c917 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2010-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2010-2019  B.A.T.M.A.N. contributors:
  *
  * Sven Eckelmann
  *
index 104784b..5e55790 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2010-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2010-2019  B.A.T.M.A.N. contributors:
  *
  * Sven Eckelmann
  *
index 8dcd496..f73d791 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich, Antonio Quartulli
  *
index 01b6c8e..61bca75 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich, Antonio Quartulli
  *
index 40e69c9..7e947b0 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index ef5867f..c0f033b 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index cbe17da..a21b34e 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2007-2018  B.A.T.M.A.N. contributors:
+/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
index 1506e16..65228bf 100644 (file)
@@ -1383,9 +1383,9 @@ static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg,
 
        if (mask & HCI_CMSG_TSTAMP) {
 #ifdef CONFIG_COMPAT
-               struct compat_timeval ctv;
+               struct old_timeval32 ctv;
 #endif
-               struct timeval tv;
+               struct __kernel_old_timeval tv;
                void *data;
                int len;
 
index 0947ee7..5d6c776 100644 (file)
@@ -5,7 +5,6 @@
 
 hostprogs-y := bpfilter_umh
 bpfilter_umh-objs := main.o
-KBUILD_HOSTCFLAGS += -I. -Itools/include/ -Itools/include/uapi
 HOSTCC := $(CC)
 
 ifeq ($(CONFIG_BPFILTER_UMH), y)
index 1317f10..61ce845 100644 (file)
@@ -6,7 +6,7 @@
 #include <sys/socket.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include "include/uapi/linux/bpf.h"
+#include "../../include/uapi/linux/bpf.h"
 #include <asm/unistd.h>
 #include "msgfmt.h"
 
index 780757b..4a048fd 100644 (file)
@@ -1018,8 +1018,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
                if (!nsrcs)
                        return -EINVAL;
 
-               grec_len = sizeof(*grec) +
-                          sizeof(struct in6_addr) * ntohs(*nsrcs);
+               grec_len = struct_size(grec, grec_src, ntohs(*nsrcs));
 
                if (!ipv6_mc_may_pull(skb, len + grec_len))
                        return -EINVAL;
@@ -1841,7 +1840,7 @@ static void br_ip4_multicast_join_snoopers(struct net_bridge *br)
        if (!in_dev)
                return;
 
-       ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP));
+       __ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC);
        in_dev_put(in_dev);
 }
 
@@ -1872,7 +1871,7 @@ static void br_ip4_multicast_leave_snoopers(struct net_bridge *br)
        if (WARN_ON(!in_dev))
                return;
 
-       ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP));
+       __ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC);
        in_dev_put(in_dev);
 }
 
index 4d2b9eb..db9e8ab 100644 (file)
@@ -14,7 +14,7 @@ static int br_switchdev_mark_get(struct net_bridge *br, struct net_device *dev)
 
        /* dev is yet to be added to the port list. */
        list_for_each_entry(p, &br->port_list, list) {
-               if (switchdev_port_same_parent_id(dev, p->dev))
+               if (netdev_port_same_parent_id(dev, p->dev))
                        return p->offload_fwd_mark;
        }
 
@@ -23,15 +23,12 @@ static int br_switchdev_mark_get(struct net_bridge *br, struct net_device *dev)
 
 int nbp_switchdev_mark_set(struct net_bridge_port *p)
 {
-       struct switchdev_attr attr = {
-               .orig_dev = p->dev,
-               .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
-       };
+       struct netdev_phys_item_id ppid = { };
        int err;
 
        ASSERT_RTNL();
 
-       err = switchdev_port_attr_get(p->dev, &attr);
+       err = dev_get_port_parent_id(p->dev, &ppid, true);
        if (err) {
                if (err == -EOPNOTSUPP)
                        return 0;
index 5e55cef..6693e20 100644 (file)
@@ -2293,9 +2293,12 @@ static int compat_do_replace(struct net *net, void __user *user,
 
        xt_compat_lock(NFPROTO_BRIDGE);
 
-       ret = xt_compat_init_offsets(NFPROTO_BRIDGE, tmp.nentries);
-       if (ret < 0)
-               goto out_unlock;
+       if (tmp.nentries) {
+               ret = xt_compat_init_offsets(NFPROTO_BRIDGE, tmp.nentries);
+               if (ret < 0)
+                       goto out_unlock;
+       }
+
        ret = compat_copy_entries(entries_tmp, tmp.entries_size, &state);
        if (ret < 0)
                goto out_unlock;
index 38c2b7a..37ac5ca 100644 (file)
@@ -319,16 +319,12 @@ struct cfpkt *cfpkt_append(struct cfpkt *dstpkt,
                if (tmppkt == NULL)
                        return NULL;
                tmp = pkt_to_skb(tmppkt);
-               skb_set_tail_pointer(tmp, dstlen);
-               tmp->len = dstlen;
-               memcpy(tmp->data, dst->data, dstlen);
+               skb_put_data(tmp, dst->data, dstlen);
                cfpkt_destroy(dstpkt);
                dst = tmp;
        }
-       memcpy(skb_tail_pointer(dst), add->data, skb_headlen(add));
+       skb_put_data(dst, add->data, skb_headlen(add));
        cfpkt_destroy(addpkt);
-       dst->tail += addlen;
-       dst->len += addlen;
        return skb_to_pkt(dst);
 }
 
@@ -359,13 +355,11 @@ struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos)
        if (skb2 == NULL)
                return NULL;
 
+       skb_put_data(skb2, split, len2nd);
+
        /* Reduce the length of the original packet */
-       skb_set_tail_pointer(skb, pos);
-       skb->len = pos;
+       skb_trim(skb, pos);
 
-       memcpy(skb2->data, split, len2nd);
-       skb2->tail += len2nd;
-       skb2->len += len2nd;
        skb2->priority = skb->priority;
        return skb_to_pkt(skb2);
 }
index 959d1c5..9629f05 100644 (file)
@@ -209,8 +209,8 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat
 {
        struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control;
        struct compat_cmsghdr cmhdr;
-       struct compat_timeval ctv;
-       struct compat_timespec cts[3];
+       struct old_timeval32 ctv;
+       struct old_timespec32 cts[3];
        int cmlen;
 
        if (cm == NULL || kmsg->msg_controllen < sizeof(*cm)) {
@@ -219,16 +219,16 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat
        }
 
        if (!COMPAT_USE_64BIT_TIME) {
-               if (level == SOL_SOCKET && type == SCM_TIMESTAMP) {
-                       struct timeval *tv = (struct timeval *)data;
+               if (level == SOL_SOCKET && type == SO_TIMESTAMP_OLD) {
+                       struct __kernel_old_timeval *tv = (struct __kernel_old_timeval *)data;
                        ctv.tv_sec = tv->tv_sec;
                        ctv.tv_usec = tv->tv_usec;
                        data = &ctv;
                        len = sizeof(ctv);
                }
                if (level == SOL_SOCKET &&
-                   (type == SCM_TIMESTAMPNS || type == SCM_TIMESTAMPING)) {
-                       int count = type == SCM_TIMESTAMPNS ? 1 : 3;
+                   (type == SO_TIMESTAMPNS_OLD || type == SO_TIMESTAMPING_OLD)) {
+                       int count = type == SO_TIMESTAMPNS_OLD ? 1 : 3;
                        int i;
                        struct timespec *ts = (struct timespec *)data;
                        for (i = 0; i < count; i++) {
@@ -348,28 +348,6 @@ static int do_set_attach_filter(struct socket *sock, int level, int optname,
                              sizeof(struct sock_fprog));
 }
 
-static int do_set_sock_timeout(struct socket *sock, int level,
-               int optname, char __user *optval, unsigned int optlen)
-{
-       struct compat_timeval __user *up = (struct compat_timeval __user *)optval;
-       struct timeval ktime;
-       mm_segment_t old_fs;
-       int err;
-
-       if (optlen < sizeof(*up))
-               return -EINVAL;
-       if (!access_ok(up, sizeof(*up)) ||
-           __get_user(ktime.tv_sec, &up->tv_sec) ||
-           __get_user(ktime.tv_usec, &up->tv_usec))
-               return -EFAULT;
-       old_fs = get_fs();
-       set_fs(KERNEL_DS);
-       err = sock_setsockopt(sock, level, optname, (char *)&ktime, sizeof(ktime));
-       set_fs(old_fs);
-
-       return err;
-}
-
 static int compat_sock_setsockopt(struct socket *sock, int level, int optname,
                                char __user *optval, unsigned int optlen)
 {
@@ -377,10 +355,6 @@ static int compat_sock_setsockopt(struct socket *sock, int level, int optname,
            optname == SO_ATTACH_REUSEPORT_CBPF)
                return do_set_attach_filter(sock, level, optname,
                                            optval, optlen);
-       if (!COMPAT_USE_64BIT_TIME &&
-           (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO))
-               return do_set_sock_timeout(sock, level, optname, optval, optlen);
-
        return sock_setsockopt(sock, level, optname, optval, optlen);
 }
 
@@ -417,44 +391,6 @@ COMPAT_SYSCALL_DEFINE5(setsockopt, int, fd, int, level, int, optname,
        return __compat_sys_setsockopt(fd, level, optname, optval, optlen);
 }
 
-static int do_get_sock_timeout(struct socket *sock, int level, int optname,
-               char __user *optval, int __user *optlen)
-{
-       struct compat_timeval __user *up;
-       struct timeval ktime;
-       mm_segment_t old_fs;
-       int len, err;
-
-       up = (struct compat_timeval __user *) optval;
-       if (get_user(len, optlen))
-               return -EFAULT;
-       if (len < sizeof(*up))
-               return -EINVAL;
-       len = sizeof(ktime);
-       old_fs = get_fs();
-       set_fs(KERNEL_DS);
-       err = sock_getsockopt(sock, level, optname, (char *) &ktime, &len);
-       set_fs(old_fs);
-
-       if (!err) {
-               if (put_user(sizeof(*up), optlen) ||
-                   !access_ok(up, sizeof(*up)) ||
-                   __put_user(ktime.tv_sec, &up->tv_sec) ||
-                   __put_user(ktime.tv_usec, &up->tv_usec))
-                       err = -EFAULT;
-       }
-       return err;
-}
-
-static int compat_sock_getsockopt(struct socket *sock, int level, int optname,
-                               char __user *optval, int __user *optlen)
-{
-       if (!COMPAT_USE_64BIT_TIME &&
-           (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO))
-               return do_get_sock_timeout(sock, level, optname, optval, optlen);
-       return sock_getsockopt(sock, level, optname, optval, optlen);
-}
-
 int compat_sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp)
 {
        struct compat_timeval __user *ctv;
@@ -527,7 +463,7 @@ static int __compat_sys_getsockopt(int fd, int level, int optname,
                }
 
                if (level == SOL_SOCKET)
-                       err = compat_sock_getsockopt(sock, level,
+                       err = sock_getsockopt(sock, level,
                                        optname, optval, optlen);
                else if (sock->ops->compat_getsockopt)
                        err = sock->ops->compat_getsockopt(sock, level,
index fccd31e..f97d625 100644 (file)
@@ -11,7 +11,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
 obj-y               += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \
                        neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
                        sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \
-                       fib_notifier.o xdp.o
+                       fib_notifier.o xdp.o flow_offload.o
 
 obj-y += net-sysfs.o
 obj-$(CONFIG_PAGE_POOL) += page_pool.o
index 82f2002..ecbe419 100644 (file)
@@ -7878,6 +7878,63 @@ int dev_get_phys_port_name(struct net_device *dev,
 EXPORT_SYMBOL(dev_get_phys_port_name);
 
 /**
+ *     dev_get_port_parent_id - Get the device's port parent identifier
+ *     @dev: network device
+ *     @ppid: pointer to a storage for the port's parent identifier
+ *     @recurse: allow/disallow recursion to lower devices
+ *
+ *     Get the devices's port parent identifier
+ */
+int dev_get_port_parent_id(struct net_device *dev,
+                          struct netdev_phys_item_id *ppid,
+                          bool recurse)
+{
+       const struct net_device_ops *ops = dev->netdev_ops;
+       struct netdev_phys_item_id first = { };
+       struct net_device *lower_dev;
+       struct list_head *iter;
+       int err = -EOPNOTSUPP;
+
+       if (ops->ndo_get_port_parent_id)
+               return ops->ndo_get_port_parent_id(dev, ppid);
+
+       if (!recurse)
+               return err;
+
+       netdev_for_each_lower_dev(dev, lower_dev, iter) {
+               err = dev_get_port_parent_id(lower_dev, ppid, recurse);
+               if (err)
+                       break;
+               if (!first.id_len)
+                       first = *ppid;
+               else if (memcmp(&first, ppid, sizeof(*ppid)))
+                       return -ENODATA;
+       }
+
+       return err;
+}
+EXPORT_SYMBOL(dev_get_port_parent_id);
+
+/**
+ *     netdev_port_same_parent_id - Indicate if two network devices have
+ *     the same port parent identifier
+ *     @a: first network device
+ *     @b: second network device
+ */
+bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b)
+{
+       struct netdev_phys_item_id a_id = { };
+       struct netdev_phys_item_id b_id = { };
+
+       if (dev_get_port_parent_id(a, &a_id, true) ||
+           dev_get_port_parent_id(b, &b_id, true))
+               return false;
+
+       return netdev_phys_item_id_same(&a_id, &b_id);
+}
+EXPORT_SYMBOL(netdev_port_same_parent_id);
+
+/**
  *     dev_change_proto_down - update protocol port state information
  *     @dev: device
  *     @proto_down: new value
@@ -7976,35 +8033,41 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
        enum bpf_netdev_command query;
        struct bpf_prog *prog = NULL;
        bpf_op_t bpf_op, bpf_chk;
+       bool offload;
        int err;
 
        ASSERT_RTNL();
 
-       query = flags & XDP_FLAGS_HW_MODE ? XDP_QUERY_PROG_HW : XDP_QUERY_PROG;
+       offload = flags & XDP_FLAGS_HW_MODE;
+       query = offload ? XDP_QUERY_PROG_HW : XDP_QUERY_PROG;
 
        bpf_op = bpf_chk = ops->ndo_bpf;
-       if (!bpf_op && (flags & (XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE)))
+       if (!bpf_op && (flags & (XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE))) {
+               NL_SET_ERR_MSG(extack, "underlying driver does not support XDP in native mode");
                return -EOPNOTSUPP;
+       }
        if (!bpf_op || (flags & XDP_FLAGS_SKB_MODE))
                bpf_op = generic_xdp_install;
        if (bpf_op == bpf_chk)
                bpf_chk = generic_xdp_install;
 
        if (fd >= 0) {
-               if (__dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG) ||
-                   __dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG_HW))
+               if (!offload && __dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG)) {
+                       NL_SET_ERR_MSG(extack, "native and generic XDP can't be active at the same time");
                        return -EEXIST;
+               }
                if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) &&
-                   __dev_xdp_query(dev, bpf_op, query))
+                   __dev_xdp_query(dev, bpf_op, query)) {
+                       NL_SET_ERR_MSG(extack, "XDP program already attached");
                        return -EBUSY;
+               }
 
                prog = bpf_prog_get_type_dev(fd, BPF_PROG_TYPE_XDP,
                                             bpf_op == ops->ndo_bpf);
                if (IS_ERR(prog))
                        return PTR_ERR(prog);
 
-               if (!(flags & XDP_FLAGS_HW_MODE) &&
-                   bpf_prog_is_dev_bound(prog->aux)) {
+               if (!offload && bpf_prog_is_dev_bound(prog->aux)) {
                        NL_SET_ERR_MSG(extack, "using device-bound program without HW_MODE flag is not supported");
                        bpf_prog_put(prog);
                        return -EINVAL;
@@ -8712,6 +8775,9 @@ int init_dummy_netdev(struct net_device *dev)
        set_bit(__LINK_STATE_PRESENT, &dev->state);
        set_bit(__LINK_STATE_START, &dev->state);
 
+       /* napi_busy_loop stats accounting wants this */
+       dev_net_set(dev, &init_net);
+
        /* Note : We dont allocate pcpu_refcnt for dummy devices,
         * because users of this 'device' dont need to change
         * its refcount.
index abb0da9..04d9855 100644 (file)
@@ -81,6 +81,7 @@ struct devlink_dpipe_header devlink_dpipe_header_ipv6 = {
 EXPORT_SYMBOL(devlink_dpipe_header_ipv6);
 
 EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg);
+EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr);
 
 static LIST_HEAD(devlink_list);
 
@@ -115,6 +116,8 @@ static struct devlink *devlink_get_from_attrs(struct net *net,
        busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]);
        devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]);
 
+       lockdep_assert_held(&devlink_mutex);
+
        list_for_each_entry(devlink, &devlink_list, list) {
                if (strcmp(devlink->dev->bus->name, busname) == 0 &&
                    strcmp(dev_name(devlink->dev), devname) == 0 &&
@@ -932,6 +935,9 @@ static int devlink_nl_sb_pool_fill(struct sk_buff *msg, struct devlink *devlink,
        if (nla_put_u8(msg, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE,
                       pool_info.threshold_type))
                goto nla_put_failure;
+       if (nla_put_u32(msg, DEVLINK_ATTR_SB_POOL_CELL_SIZE,
+                       pool_info.cell_size))
+               goto nla_put_failure;
 
        genlmsg_end(msg, hdr);
        return 0;
@@ -2656,6 +2662,27 @@ static int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info)
        return devlink->ops->reload(devlink, info->extack);
 }
 
+static int devlink_nl_cmd_flash_update(struct sk_buff *skb,
+                                      struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       const char *file_name, *component;
+       struct nlattr *nla_component;
+
+       if (!devlink->ops->flash_update)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME])
+               return -EINVAL;
+       file_name = nla_data(info->attrs[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME]);
+
+       nla_component = info->attrs[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT];
+       component = nla_component ? nla_data(nla_component) : NULL;
+
+       return devlink->ops->flash_update(devlink, file_name, component,
+                                         info->extack);
+}
+
 static const struct devlink_param devlink_param_generic[] = {
        {
                .id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,
@@ -2843,11 +2870,13 @@ nla_put_failure:
 }
 
 static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
+                                unsigned int port_index,
                                 struct devlink_param_item *param_item,
                                 enum devlink_command cmd,
                                 u32 portid, u32 seq, int flags)
 {
        union devlink_param_value param_value[DEVLINK_PARAM_CMODE_MAX + 1];
+       bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};
        const struct devlink_param *param = param_item->param;
        struct devlink_param_gset_ctx ctx;
        struct nlattr *param_values_list;
@@ -2866,12 +2895,15 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
                                return -EOPNOTSUPP;
                        param_value[i] = param_item->driverinit_value;
                } else {
+                       if (!param_item->published)
+                               continue;
                        ctx.cmode = i;
                        err = devlink_param_get(devlink, param, &ctx);
                        if (err)
                                return err;
                        param_value[i] = ctx.val;
                }
+               param_value_set[i] = true;
        }
 
        hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
@@ -2880,6 +2912,13 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
 
        if (devlink_nl_put_handle(msg, devlink))
                goto genlmsg_cancel;
+
+       if (cmd == DEVLINK_CMD_PORT_PARAM_GET ||
+           cmd == DEVLINK_CMD_PORT_PARAM_NEW ||
+           cmd == DEVLINK_CMD_PORT_PARAM_DEL)
+               if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, port_index))
+                       goto genlmsg_cancel;
+
        param_attr = nla_nest_start(msg, DEVLINK_ATTR_PARAM);
        if (!param_attr)
                goto genlmsg_cancel;
@@ -2899,7 +2938,7 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
                goto param_nest_cancel;
 
        for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
-               if (!devlink_param_cmode_is_supported(param, i))
+               if (!param_value_set[i])
                        continue;
                err = devlink_nl_param_value_fill_one(msg, param->type,
                                                      i, param_value[i]);
@@ -2922,18 +2961,22 @@ genlmsg_cancel:
 }
 
 static void devlink_param_notify(struct devlink *devlink,
+                                unsigned int port_index,
                                 struct devlink_param_item *param_item,
                                 enum devlink_command cmd)
 {
        struct sk_buff *msg;
        int err;
 
-       WARN_ON(cmd != DEVLINK_CMD_PARAM_NEW && cmd != DEVLINK_CMD_PARAM_DEL);
+       WARN_ON(cmd != DEVLINK_CMD_PARAM_NEW && cmd != DEVLINK_CMD_PARAM_DEL &&
+               cmd != DEVLINK_CMD_PORT_PARAM_NEW &&
+               cmd != DEVLINK_CMD_PORT_PARAM_DEL);
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
-       err = devlink_nl_param_fill(msg, devlink, param_item, cmd, 0, 0, 0);
+       err = devlink_nl_param_fill(msg, devlink, port_index, param_item, cmd,
+                                   0, 0, 0);
        if (err) {
                nlmsg_free(msg);
                return;
@@ -2962,7 +3005,7 @@ static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg,
                                idx++;
                                continue;
                        }
-                       err = devlink_nl_param_fill(msg, devlink, param_item,
+                       err = devlink_nl_param_fill(msg, devlink, 0, param_item,
                                                    DEVLINK_CMD_PARAM_GET,
                                                    NETLINK_CB(cb->skb).portid,
                                                    cb->nlh->nlmsg_seq,
@@ -3051,7 +3094,7 @@ devlink_param_value_get_from_info(const struct devlink_param *param,
 }
 
 static struct devlink_param_item *
-devlink_param_get_from_info(struct devlink *devlink,
+devlink_param_get_from_info(struct list_head *param_list,
                            struct genl_info *info)
 {
        char *param_name;
@@ -3060,7 +3103,7 @@ devlink_param_get_from_info(struct devlink *devlink,
                return NULL;
 
        param_name = nla_data(info->attrs[DEVLINK_ATTR_PARAM_NAME]);
-       return devlink_param_find_by_name(&devlink->param_list, param_name);
+       return devlink_param_find_by_name(param_list, param_name);
 }
 
 static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
@@ -3071,7 +3114,7 @@ static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
        struct sk_buff *msg;
        int err;
 
-       param_item = devlink_param_get_from_info(devlink, info);
+       param_item = devlink_param_get_from_info(&devlink->param_list, info);
        if (!param_item)
                return -EINVAL;
 
@@ -3079,7 +3122,7 @@ static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
        if (!msg)
                return -ENOMEM;
 
-       err = devlink_nl_param_fill(msg, devlink, param_item,
+       err = devlink_nl_param_fill(msg, devlink, 0, param_item,
                                    DEVLINK_CMD_PARAM_GET,
                                    info->snd_portid, info->snd_seq, 0);
        if (err) {
@@ -3090,10 +3133,12 @@ static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
        return genlmsg_reply(msg, info);
 }
 
-static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
-                                        struct genl_info *info)
+static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
+                                          unsigned int port_index,
+                                          struct list_head *param_list,
+                                          struct genl_info *info,
+                                          enum devlink_command cmd)
 {
-       struct devlink *devlink = info->user_ptr[0];
        enum devlink_param_type param_type;
        struct devlink_param_gset_ctx ctx;
        enum devlink_param_cmode cmode;
@@ -3102,7 +3147,7 @@ static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
        union devlink_param_value value;
        int err = 0;
 
-       param_item = devlink_param_get_from_info(devlink, info);
+       param_item = devlink_param_get_from_info(param_list, info);
        if (!param_item)
                return -EINVAL;
        param = param_item->param;
@@ -3142,17 +3187,28 @@ static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
                        return err;
        }
 
-       devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
+       devlink_param_notify(devlink, port_index, param_item, cmd);
        return 0;
 }
 
+static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
+                                        struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+
+       return __devlink_nl_cmd_param_set_doit(devlink, 0, &devlink->param_list,
+                                              info, DEVLINK_CMD_PARAM_NEW);
+}
+
 static int devlink_param_register_one(struct devlink *devlink,
-                                     const struct devlink_param *param)
+                                     unsigned int port_index,
+                                     struct list_head *param_list,
+                                     const struct devlink_param *param,
+                                     enum devlink_command cmd)
 {
        struct devlink_param_item *param_item;
 
-       if (devlink_param_find_by_name(&devlink->param_list,
-                                      param->name))
+       if (devlink_param_find_by_name(param_list, param->name))
                return -EEXIST;
 
        if (param->supported_cmodes == BIT(DEVLINK_PARAM_CMODE_DRIVERINIT))
@@ -3165,24 +3221,111 @@ static int devlink_param_register_one(struct devlink *devlink,
                return -ENOMEM;
        param_item->param = param;
 
-       list_add_tail(&param_item->list, &devlink->param_list);
-       devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
+       list_add_tail(&param_item->list, param_list);
+       devlink_param_notify(devlink, port_index, param_item, cmd);
        return 0;
 }
 
 static void devlink_param_unregister_one(struct devlink *devlink,
-                                        const struct devlink_param *param)
+                                        unsigned int port_index,
+                                        struct list_head *param_list,
+                                        const struct devlink_param *param,
+                                        enum devlink_command cmd)
 {
        struct devlink_param_item *param_item;
 
-       param_item = devlink_param_find_by_name(&devlink->param_list,
-                                               param->name);
+       param_item = devlink_param_find_by_name(param_list, param->name);
        WARN_ON(!param_item);
-       devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_DEL);
+       devlink_param_notify(devlink, port_index, param_item, cmd);
        list_del(&param_item->list);
        kfree(param_item);
 }
 
+static int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg,
+                                               struct netlink_callback *cb)
+{
+       struct devlink_param_item *param_item;
+       struct devlink_port *devlink_port;
+       struct devlink *devlink;
+       int start = cb->args[0];
+       int idx = 0;
+       int err;
+
+       mutex_lock(&devlink_mutex);
+       list_for_each_entry(devlink, &devlink_list, list) {
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       continue;
+               mutex_lock(&devlink->lock);
+               list_for_each_entry(devlink_port, &devlink->port_list, list) {
+                       list_for_each_entry(param_item,
+                                           &devlink_port->param_list, list) {
+                               if (idx < start) {
+                                       idx++;
+                                       continue;
+                               }
+                               err = devlink_nl_param_fill(msg,
+                                               devlink_port->devlink,
+                                               devlink_port->index, param_item,
+                                               DEVLINK_CMD_PORT_PARAM_GET,
+                                               NETLINK_CB(cb->skb).portid,
+                                               cb->nlh->nlmsg_seq,
+                                               NLM_F_MULTI);
+                               if (err) {
+                                       mutex_unlock(&devlink->lock);
+                                       goto out;
+                               }
+                               idx++;
+                       }
+               }
+               mutex_unlock(&devlink->lock);
+       }
+out:
+       mutex_unlock(&devlink_mutex);
+
+       cb->args[0] = idx;
+       return msg->len;
+}
+
+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_param_item *param_item;
+       struct sk_buff *msg;
+       int err;
+
+       param_item = devlink_param_get_from_info(&devlink_port->param_list,
+                                                info);
+       if (!param_item)
+               return -EINVAL;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       err = devlink_nl_param_fill(msg, devlink_port->devlink,
+                                   devlink_port->index, param_item,
+                                   DEVLINK_CMD_PORT_PARAM_GET,
+                                   info->snd_portid, info->snd_seq, 0);
+       if (err) {
+               nlmsg_free(msg);
+               return err;
+       }
+
+       return genlmsg_reply(msg, info);
+}
+
+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];
+
+       return __devlink_nl_cmd_param_set_doit(devlink_port->devlink,
+                                              devlink_port->index,
+                                              &devlink_port->param_list, info,
+                                              DEVLINK_CMD_PORT_PARAM_NEW);
+}
+
 static int devlink_nl_region_snapshot_id_put(struct sk_buff *msg,
                                             struct devlink *devlink,
                                             struct devlink_snapshot *snapshot)
@@ -3504,44 +3647,56 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
                                             struct netlink_callback *cb)
 {
        u64 ret_offset, start_offset, end_offset = 0;
-       struct nlattr *attrs[DEVLINK_ATTR_MAX + 1];
        const struct genl_ops *ops = cb->data;
        struct devlink_region *region;
        struct nlattr *chunks_attr;
        const char *region_name;
        struct devlink *devlink;
+       struct nlattr **attrs;
        bool dump = true;
        void *hdr;
        int err;
 
        start_offset = *((u64 *)&cb->args[0]);
 
+       attrs = kmalloc_array(DEVLINK_ATTR_MAX + 1, sizeof(*attrs), GFP_KERNEL);
+       if (!attrs)
+               return -ENOMEM;
+
        err = nlmsg_parse(cb->nlh, GENL_HDRLEN + devlink_nl_family.hdrsize,
                          attrs, DEVLINK_ATTR_MAX, ops->policy, cb->extack);
        if (err)
-               goto out;
+               goto out_free;
 
+       mutex_lock(&devlink_mutex);
        devlink = devlink_get_from_attrs(sock_net(cb->skb->sk), attrs);
-       if (IS_ERR(devlink))
-               goto out;
+       if (IS_ERR(devlink)) {
+               err = PTR_ERR(devlink);
+               goto out_dev;
+       }
 
-       mutex_lock(&devlink_mutex);
        mutex_lock(&devlink->lock);
 
        if (!attrs[DEVLINK_ATTR_REGION_NAME] ||
-           !attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID])
+           !attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) {
+               err = -EINVAL;
                goto out_unlock;
+       }
 
        region_name = nla_data(attrs[DEVLINK_ATTR_REGION_NAME]);
        region = devlink_region_get_by_name(devlink, region_name);
-       if (!region)
+       if (!region) {
+               err = -EINVAL;
                goto out_unlock;
+       }
 
        hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
                          &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI,
                          DEVLINK_CMD_REGION_READ);
-       if (!hdr)
+       if (!hdr) {
+               err = -EMSGSIZE;
                goto out_unlock;
+       }
 
        err = devlink_nl_put_handle(skb, devlink);
        if (err)
@@ -3552,8 +3707,10 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
                goto nla_put_failure;
 
        chunks_attr = nla_nest_start(skb, DEVLINK_ATTR_REGION_CHUNKS);
-       if (!chunks_attr)
+       if (!chunks_attr) {
+               err = -EMSGSIZE;
                goto nla_put_failure;
+       }
 
        if (attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR] &&
            attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]) {
@@ -3576,8 +3733,10 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
                goto nla_put_failure;
 
        /* Check if there was any progress done to prevent infinite loop */
-       if (ret_offset == start_offset)
+       if (ret_offset == start_offset) {
+               err = -EINVAL;
                goto nla_put_failure;
+       }
 
        *((u64 *)&cb->args[0]) = ret_offset;
 
@@ -3585,6 +3744,7 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
        genlmsg_end(skb, hdr);
        mutex_unlock(&devlink->lock);
        mutex_unlock(&devlink_mutex);
+       kfree(attrs);
 
        return skb->len;
 
@@ -3592,62 +3752,1186 @@ nla_put_failure:
        genlmsg_cancel(skb, hdr);
 out_unlock:
        mutex_unlock(&devlink->lock);
+out_dev:
        mutex_unlock(&devlink_mutex);
-out:
+out_free:
+       kfree(attrs);
+       return err;
+}
+
+struct devlink_info_req {
+       struct sk_buff *msg;
+};
+
+int devlink_info_driver_name_put(struct devlink_info_req *req, const char *name)
+{
+       return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME, name);
+}
+EXPORT_SYMBOL_GPL(devlink_info_driver_name_put);
+
+int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn)
+{
+       return nla_put_string(req->msg, DEVLINK_ATTR_INFO_SERIAL_NUMBER, sn);
+}
+EXPORT_SYMBOL_GPL(devlink_info_serial_number_put);
+
+static int devlink_info_version_put(struct devlink_info_req *req, int attr,
+                                   const char *version_name,
+                                   const char *version_value)
+{
+       struct nlattr *nest;
+       int err;
+
+       nest = nla_nest_start(req->msg, attr);
+       if (!nest)
+               return -EMSGSIZE;
+
+       err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_NAME,
+                            version_name);
+       if (err)
+               goto nla_put_failure;
+
+       err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_VALUE,
+                            version_value);
+       if (err)
+               goto nla_put_failure;
+
+       nla_nest_end(req->msg, nest);
+
+       return 0;
+
+nla_put_failure:
+       nla_nest_cancel(req->msg, nest);
+       return err;
+}
+
+int devlink_info_version_fixed_put(struct devlink_info_req *req,
+                                  const char *version_name,
+                                  const char *version_value)
+{
+       return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_FIXED,
+                                       version_name, version_value);
+}
+EXPORT_SYMBOL_GPL(devlink_info_version_fixed_put);
+
+int devlink_info_version_stored_put(struct devlink_info_req *req,
+                                   const char *version_name,
+                                   const char *version_value)
+{
+       return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_STORED,
+                                       version_name, version_value);
+}
+EXPORT_SYMBOL_GPL(devlink_info_version_stored_put);
+
+int devlink_info_version_running_put(struct devlink_info_req *req,
+                                    const char *version_name,
+                                    const char *version_value)
+{
+       return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_RUNNING,
+                                       version_name, version_value);
+}
+EXPORT_SYMBOL_GPL(devlink_info_version_running_put);
+
+static int
+devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
+                    enum devlink_command cmd, u32 portid,
+                    u32 seq, int flags, struct netlink_ext_ack *extack)
+{
+       struct devlink_info_req req;
+       void *hdr;
+       int err;
+
+       hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
+       if (!hdr)
+               return -EMSGSIZE;
+
+       err = -EMSGSIZE;
+       if (devlink_nl_put_handle(msg, devlink))
+               goto err_cancel_msg;
+
+       req.msg = msg;
+       err = devlink->ops->info_get(devlink, &req, extack);
+       if (err)
+               goto err_cancel_msg;
+
+       genlmsg_end(msg, hdr);
        return 0;
+
+err_cancel_msg:
+       genlmsg_cancel(msg, hdr);
+       return err;
 }
 
-static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
-       [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
-       [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },
-       [DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 },
-       [DEVLINK_ATTR_PORT_TYPE] = { .type = NLA_U16 },
-       [DEVLINK_ATTR_PORT_SPLIT_COUNT] = { .type = NLA_U32 },
-       [DEVLINK_ATTR_SB_INDEX] = { .type = NLA_U32 },
-       [DEVLINK_ATTR_SB_POOL_INDEX] = { .type = NLA_U16 },
-       [DEVLINK_ATTR_SB_POOL_TYPE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_SB_POOL_SIZE] = { .type = NLA_U32 },
-       [DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_SB_THRESHOLD] = { .type = NLA_U32 },
-       [DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 },
-       [DEVLINK_ATTR_ESWITCH_MODE] = { .type = NLA_U16 },
-       [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING },
-       [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_RESOURCE_ID] = { .type = NLA_U64},
-       [DEVLINK_ATTR_RESOURCE_SIZE] = { .type = NLA_U64},
-       [DEVLINK_ATTR_PARAM_NAME] = { .type = NLA_NUL_STRING },
-       [DEVLINK_ATTR_PARAM_TYPE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_PARAM_VALUE_CMODE] = { .type = NLA_U8 },
-       [DEVLINK_ATTR_REGION_NAME] = { .type = NLA_NUL_STRING },
-       [DEVLINK_ATTR_REGION_SNAPSHOT_ID] = { .type = NLA_U32 },
+static int devlink_nl_cmd_info_get_doit(struct sk_buff *skb,
+                                       struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct sk_buff *msg;
+       int err;
+
+       if (!devlink->ops || !devlink->ops->info_get)
+               return -EOPNOTSUPP;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
+                                  info->snd_portid, info->snd_seq, 0,
+                                  info->extack);
+       if (err) {
+               nlmsg_free(msg);
+               return err;
+       }
+
+       return genlmsg_reply(msg, info);
+}
+
+static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg,
+                                         struct netlink_callback *cb)
+{
+       struct devlink *devlink;
+       int start = cb->args[0];
+       int idx = 0;
+       int err;
+
+       mutex_lock(&devlink_mutex);
+       list_for_each_entry(devlink, &devlink_list, list) {
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       continue;
+               if (idx < start) {
+                       idx++;
+                       continue;
+               }
+
+               mutex_lock(&devlink->lock);
+               err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
+                                          NETLINK_CB(cb->skb).portid,
+                                          cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                          cb->extack);
+               mutex_unlock(&devlink->lock);
+               if (err)
+                       break;
+               idx++;
+       }
+       mutex_unlock(&devlink_mutex);
+
+       cb->args[0] = idx;
+       return msg->len;
+}
+
+struct devlink_fmsg_item {
+       struct list_head list;
+       int attrtype;
+       u8 nla_type;
+       u16 len;
+       int value[0];
 };
 
-static const struct genl_ops devlink_nl_ops[] = {
-       {
-               .cmd = DEVLINK_CMD_GET,
-               .doit = devlink_nl_cmd_get_doit,
-               .dumpit = devlink_nl_cmd_get_dumpit,
-               .policy = devlink_nl_policy,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = DEVLINK_CMD_PORT_GET,
-               .doit = devlink_nl_cmd_port_get_doit,
-               .dumpit = devlink_nl_cmd_port_get_dumpit,
-               .policy = devlink_nl_policy,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = DEVLINK_CMD_PORT_SET,
-               .doit = devlink_nl_cmd_port_set_doit,
-               .policy = devlink_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
-       },
+struct devlink_fmsg {
+       struct list_head item_list;
+};
+
+static struct devlink_fmsg *devlink_fmsg_alloc(void)
+{
+       struct devlink_fmsg *fmsg;
+
+       fmsg = kzalloc(sizeof(*fmsg), GFP_KERNEL);
+       if (!fmsg)
+               return NULL;
+
+       INIT_LIST_HEAD(&fmsg->item_list);
+
+       return fmsg;
+}
+
+static void devlink_fmsg_free(struct devlink_fmsg *fmsg)
+{
+       struct devlink_fmsg_item *item, *tmp;
+
+       list_for_each_entry_safe(item, tmp, &fmsg->item_list, list) {
+               list_del(&item->list);
+               kfree(item);
+       }
+       kfree(fmsg);
+}
+
+static int devlink_fmsg_nest_common(struct devlink_fmsg *fmsg,
+                                   int attrtype)
+{
+       struct devlink_fmsg_item *item;
+
+       item = kzalloc(sizeof(*item), GFP_KERNEL);
+       if (!item)
+               return -ENOMEM;
+
+       item->attrtype = attrtype;
+       list_add_tail(&item->list, &fmsg->item_list);
+
+       return 0;
+}
+
+int devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg)
+{
+       return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start);
+
+static int devlink_fmsg_nest_end(struct devlink_fmsg *fmsg)
+{
+       return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END);
+}
+
+int devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg)
+{
+       return devlink_fmsg_nest_end(fmsg);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end);
+
+#define DEVLINK_FMSG_MAX_SIZE (GENLMSG_DEFAULT_SIZE - GENL_HDRLEN - NLA_HDRLEN)
+
+static int devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name)
+{
+       struct devlink_fmsg_item *item;
+
+       if (strlen(name) + 1 > DEVLINK_FMSG_MAX_SIZE)
+               return -EMSGSIZE;
+
+       item = kzalloc(sizeof(*item) + strlen(name) + 1, GFP_KERNEL);
+       if (!item)
+               return -ENOMEM;
+
+       item->nla_type = NLA_NUL_STRING;
+       item->len = strlen(name) + 1;
+       item->attrtype = DEVLINK_ATTR_FMSG_OBJ_NAME;
+       memcpy(&item->value, name, item->len);
+       list_add_tail(&item->list, &fmsg->item_list);
+
+       return 0;
+}
+
+int devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name)
+{
+       int err;
+
+       err = devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_PAIR_NEST_START);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_put_name(fmsg, name);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_start);
+
+int devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg)
+{
+       return devlink_fmsg_nest_end(fmsg);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_end);
+
+int devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg,
+                                    const char *name)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_ARR_NEST_START);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_start);
+
+int devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg)
+{
+       int err;
+
+       err = devlink_fmsg_nest_end(fmsg);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end);
+
+static int devlink_fmsg_put_value(struct devlink_fmsg *fmsg,
+                                 const void *value, u16 value_len,
+                                 u8 value_nla_type)
+{
+       struct devlink_fmsg_item *item;
+
+       if (value_len > DEVLINK_FMSG_MAX_SIZE)
+               return -EMSGSIZE;
+
+       item = kzalloc(sizeof(*item) + value_len, GFP_KERNEL);
+       if (!item)
+               return -ENOMEM;
+
+       item->nla_type = value_nla_type;
+       item->len = value_len;
+       item->attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
+       memcpy(&item->value, value, item->len);
+       list_add_tail(&item->list, &fmsg->item_list);
+
+       return 0;
+}
+
+int devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value)
+{
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_bool_put);
+
+int devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value)
+{
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u8_put);
+
+int devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value)
+{
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put);
+
+int devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value)
+{
+       return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u64_put);
+
+int devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value)
+{
+       return devlink_fmsg_put_value(fmsg, value, strlen(value) + 1,
+                                     NLA_NUL_STRING);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_string_put);
+
+int devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value,
+                           u16 value_len)
+{
+       return devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY);
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put);
+
+int devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                              bool value)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_bool_put(fmsg, value);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_pair_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_bool_pair_put);
+
+int devlink_fmsg_u8_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                            u8 value)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_u8_put(fmsg, value);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_pair_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u8_pair_put);
+
+int devlink_fmsg_u32_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                             u32 value)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_u32_put(fmsg, value);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_pair_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u32_pair_put);
+
+int devlink_fmsg_u64_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                             u64 value)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_u64_put(fmsg, value);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_pair_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_u64_pair_put);
+
+int devlink_fmsg_string_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                                const char *value)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_string_put(fmsg, value);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_pair_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_string_pair_put);
+
+int devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name,
+                                const void *value, u16 value_len)
+{
+       int err;
+
+       err = devlink_fmsg_pair_nest_start(fmsg, name);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_binary_put(fmsg, value, value_len);
+       if (err)
+               return err;
+
+       err = devlink_fmsg_pair_nest_end(fmsg);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put);
+
+static int
+devlink_fmsg_item_fill_type(struct devlink_fmsg_item *msg, struct sk_buff *skb)
+{
+       switch (msg->nla_type) {
+       case NLA_FLAG:
+       case NLA_U8:
+       case NLA_U32:
+       case NLA_U64:
+       case NLA_NUL_STRING:
+       case NLA_BINARY:
+               return nla_put_u8(skb, DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE,
+                                 msg->nla_type);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int
+devlink_fmsg_item_fill_data(struct devlink_fmsg_item *msg, struct sk_buff *skb)
+{
+       int attrtype = DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA;
+       u8 tmp;
+
+       switch (msg->nla_type) {
+       case NLA_FLAG:
+               /* Always provide flag data, regardless of its value */
+               tmp = *(bool *) msg->value;
+
+               return nla_put_u8(skb, attrtype, tmp);
+       case NLA_U8:
+               return nla_put_u8(skb, attrtype, *(u8 *) msg->value);
+       case NLA_U32:
+               return nla_put_u32(skb, attrtype, *(u32 *) msg->value);
+       case NLA_U64:
+               return nla_put_u64_64bit(skb, attrtype, *(u64 *) msg->value,
+                                        DEVLINK_ATTR_PAD);
+       case NLA_NUL_STRING:
+               return nla_put_string(skb, attrtype, (char *) &msg->value);
+       case NLA_BINARY:
+               return nla_put(skb, attrtype, msg->len, (void *) &msg->value);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int
+devlink_fmsg_prepare_skb(struct devlink_fmsg *fmsg, struct sk_buff *skb,
+                        int *start)
+{
+       struct devlink_fmsg_item *item;
+       struct nlattr *fmsg_nlattr;
+       int i = 0;
+       int err;
+
+       fmsg_nlattr = nla_nest_start(skb, DEVLINK_ATTR_FMSG);
+       if (!fmsg_nlattr)
+               return -EMSGSIZE;
+
+       list_for_each_entry(item, &fmsg->item_list, list) {
+               if (i < *start) {
+                       i++;
+                       continue;
+               }
+
+               switch (item->attrtype) {
+               case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
+               case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
+               case DEVLINK_ATTR_FMSG_ARR_NEST_START:
+               case DEVLINK_ATTR_FMSG_NEST_END:
+                       err = nla_put_flag(skb, item->attrtype);
+                       break;
+               case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
+                       err = devlink_fmsg_item_fill_type(item, skb);
+                       if (err)
+                               break;
+                       err = devlink_fmsg_item_fill_data(item, skb);
+                       break;
+               case DEVLINK_ATTR_FMSG_OBJ_NAME:
+                       err = nla_put_string(skb, item->attrtype,
+                                            (char *) &item->value);
+                       break;
+               default:
+                       err = -EINVAL;
+                       break;
+               }
+               if (!err)
+                       *start = ++i;
+               else
+                       break;
+       }
+
+       nla_nest_end(skb, fmsg_nlattr);
+       return err;
+}
+
+static int devlink_fmsg_snd(struct devlink_fmsg *fmsg,
+                           struct genl_info *info,
+                           enum devlink_command cmd, int flags)
+{
+       struct nlmsghdr *nlh;
+       struct sk_buff *skb;
+       bool last = false;
+       int index = 0;
+       void *hdr;
+       int err;
+
+       while (!last) {
+               int tmp_index = index;
+
+               skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+               if (!skb)
+                       return -ENOMEM;
+
+               hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
+                                 &devlink_nl_family, flags | NLM_F_MULTI, cmd);
+               if (!hdr) {
+                       err = -EMSGSIZE;
+                       goto nla_put_failure;
+               }
+
+               err = devlink_fmsg_prepare_skb(fmsg, skb, &index);
+               if (!err)
+                       last = true;
+               else if (err != -EMSGSIZE || tmp_index == index)
+                       goto nla_put_failure;
+
+               genlmsg_end(skb, hdr);
+               err = genlmsg_reply(skb, info);
+               if (err)
+                       return err;
+       }
+
+       skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+       nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
+                       NLMSG_DONE, 0, flags | NLM_F_MULTI);
+       if (!nlh) {
+               err = -EMSGSIZE;
+               goto nla_put_failure;
+       }
+
+       return genlmsg_reply(skb, info);
+
+nla_put_failure:
+       nlmsg_free(skb);
+       return err;
+}
+
+struct devlink_health_reporter {
+       struct list_head list;
+       void *priv;
+       const struct devlink_health_reporter_ops *ops;
+       struct devlink *devlink;
+       struct devlink_fmsg *dump_fmsg;
+       struct mutex dump_lock; /* lock parallel read/write from dump buffers */
+       u64 graceful_period;
+       bool auto_recover;
+       u8 health_state;
+       u64 dump_ts;
+       u64 error_count;
+       u64 recovery_count;
+       u64 last_recovery_ts;
+};
+
+enum devlink_health_reporter_state {
+       DEVLINK_HEALTH_REPORTER_STATE_HEALTHY,
+       DEVLINK_HEALTH_REPORTER_STATE_ERROR,
+};
+
+void *
+devlink_health_reporter_priv(struct devlink_health_reporter *reporter)
+{
+       return reporter->priv;
+}
+EXPORT_SYMBOL_GPL(devlink_health_reporter_priv);
+
+static struct devlink_health_reporter *
+devlink_health_reporter_find_by_name(struct devlink *devlink,
+                                    const char *reporter_name)
+{
+       struct devlink_health_reporter *reporter;
+
+       list_for_each_entry(reporter, &devlink->reporter_list, list)
+               if (!strcmp(reporter->ops->name, reporter_name))
+                       return reporter;
+       return NULL;
+}
+
+/**
+ *     devlink_health_reporter_create - create devlink health reporter
+ *
+ *     @devlink: devlink
+ *     @ops: ops
+ *     @graceful_period: to avoid recovery loops, in msecs
+ *     @auto_recover: auto recover when error occurs
+ *     @priv: priv
+ */
+struct devlink_health_reporter *
+devlink_health_reporter_create(struct devlink *devlink,
+                              const struct devlink_health_reporter_ops *ops,
+                              u64 graceful_period, bool auto_recover,
+                              void *priv)
+{
+       struct devlink_health_reporter *reporter;
+
+       mutex_lock(&devlink->lock);
+       if (devlink_health_reporter_find_by_name(devlink, ops->name)) {
+               reporter = ERR_PTR(-EEXIST);
+               goto unlock;
+       }
+
+       if (WARN_ON(auto_recover && !ops->recover) ||
+           WARN_ON(graceful_period && !ops->recover)) {
+               reporter = ERR_PTR(-EINVAL);
+               goto unlock;
+       }
+
+       reporter = kzalloc(sizeof(*reporter), GFP_KERNEL);
+       if (!reporter) {
+               reporter = ERR_PTR(-ENOMEM);
+               goto unlock;
+       }
+
+       reporter->priv = priv;
+       reporter->ops = ops;
+       reporter->devlink = devlink;
+       reporter->graceful_period = graceful_period;
+       reporter->auto_recover = auto_recover;
+       mutex_init(&reporter->dump_lock);
+       list_add_tail(&reporter->list, &devlink->reporter_list);
+unlock:
+       mutex_unlock(&devlink->lock);
+       return reporter;
+}
+EXPORT_SYMBOL_GPL(devlink_health_reporter_create);
+
+/**
+ *     devlink_health_reporter_destroy - destroy devlink health reporter
+ *
+ *     @reporter: devlink health reporter to destroy
+ */
+void
+devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
+{
+       mutex_lock(&reporter->devlink->lock);
+       list_del(&reporter->list);
+       mutex_unlock(&reporter->devlink->lock);
+       if (reporter->dump_fmsg)
+               devlink_fmsg_free(reporter->dump_fmsg);
+       kfree(reporter);
+}
+EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
+
+static int
+devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
+                               void *priv_ctx)
+{
+       int err;
+
+       if (!reporter->ops->recover)
+               return -EOPNOTSUPP;
+
+       err = reporter->ops->recover(reporter, priv_ctx);
+       if (err)
+               return err;
+
+       reporter->recovery_count++;
+       reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
+       reporter->last_recovery_ts = jiffies;
+
+       return 0;
+}
+
+static void
+devlink_health_dump_clear(struct devlink_health_reporter *reporter)
+{
+       if (!reporter->dump_fmsg)
+               return;
+       devlink_fmsg_free(reporter->dump_fmsg);
+       reporter->dump_fmsg = NULL;
+}
+
+static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
+                                 void *priv_ctx)
+{
+       int err;
+
+       if (!reporter->ops->dump)
+               return 0;
+
+       if (reporter->dump_fmsg)
+               return 0;
+
+       reporter->dump_fmsg = devlink_fmsg_alloc();
+       if (!reporter->dump_fmsg) {
+               err = -ENOMEM;
+               return err;
+       }
+
+       err = devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
+       if (err)
+               goto dump_err;
+
+       err = reporter->ops->dump(reporter, reporter->dump_fmsg,
+                                 priv_ctx);
+       if (err)
+               goto dump_err;
+
+       err = devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
+       if (err)
+               goto dump_err;
+
+       reporter->dump_ts = jiffies;
+
+       return 0;
+
+dump_err:
+       devlink_health_dump_clear(reporter);
+       return err;
+}
+
+int devlink_health_report(struct devlink_health_reporter *reporter,
+                         const char *msg, void *priv_ctx)
+{
+       struct devlink *devlink = reporter->devlink;
+
+       /* write a log message of the current error */
+       WARN_ON(!msg);
+       trace_devlink_health_report(devlink, reporter->ops->name, msg);
+       reporter->error_count++;
+
+       /* abort if the previous error wasn't recovered */
+       if (reporter->auto_recover &&
+           (reporter->health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
+            jiffies - reporter->last_recovery_ts <
+            msecs_to_jiffies(reporter->graceful_period))) {
+               trace_devlink_health_recover_aborted(devlink,
+                                                    reporter->ops->name,
+                                                    reporter->health_state,
+                                                    jiffies -
+                                                    reporter->last_recovery_ts);
+               return -ECANCELED;
+       }
+
+       reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
+
+       mutex_lock(&reporter->dump_lock);
+       /* store current dump of current error, for later analysis */
+       devlink_health_do_dump(reporter, priv_ctx);
+       mutex_unlock(&reporter->dump_lock);
+
+       if (reporter->auto_recover)
+               return devlink_health_reporter_recover(reporter, priv_ctx);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_health_report);
+
+static struct devlink_health_reporter *
+devlink_health_reporter_get_from_info(struct devlink *devlink,
+                                     struct genl_info *info)
+{
+       char *reporter_name;
+
+       if (!info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
+               return NULL;
+
+       reporter_name =
+               nla_data(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
+       return devlink_health_reporter_find_by_name(devlink, reporter_name);
+}
+
+static int
+devlink_nl_health_reporter_fill(struct sk_buff *msg,
+                               struct devlink *devlink,
+                               struct devlink_health_reporter *reporter,
+                               enum devlink_command cmd, u32 portid,
+                               u32 seq, int flags)
+{
+       struct nlattr *reporter_attr;
+       void *hdr;
+
+       hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
+       if (!hdr)
+               return -EMSGSIZE;
+
+       if (devlink_nl_put_handle(msg, devlink))
+               goto genlmsg_cancel;
+
+       reporter_attr = nla_nest_start(msg, DEVLINK_ATTR_HEALTH_REPORTER);
+       if (!reporter_attr)
+               goto genlmsg_cancel;
+       if (nla_put_string(msg, DEVLINK_ATTR_HEALTH_REPORTER_NAME,
+                          reporter->ops->name))
+               goto reporter_nest_cancel;
+       if (nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_STATE,
+                      reporter->health_state))
+               goto reporter_nest_cancel;
+       if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_ERR,
+                             reporter->error_count, DEVLINK_ATTR_PAD))
+               goto reporter_nest_cancel;
+       if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_RECOVER,
+                             reporter->recovery_count, DEVLINK_ATTR_PAD))
+               goto reporter_nest_cancel;
+       if (nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
+                             reporter->graceful_period,
+                             DEVLINK_ATTR_PAD))
+               goto reporter_nest_cancel;
+       if (nla_put_u8(msg, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
+                      reporter->auto_recover))
+               goto reporter_nest_cancel;
+       if (reporter->dump_fmsg &&
+           nla_put_u64_64bit(msg, DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS,
+                             jiffies_to_msecs(reporter->dump_ts),
+                             DEVLINK_ATTR_PAD))
+               goto reporter_nest_cancel;
+
+       nla_nest_end(msg, reporter_attr);
+       genlmsg_end(msg, hdr);
+       return 0;
+
+reporter_nest_cancel:
+       nla_nest_end(msg, reporter_attr);
+genlmsg_cancel:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int devlink_nl_cmd_health_reporter_get_doit(struct sk_buff *skb,
+                                                  struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+       struct sk_buff *msg;
+       int err;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       err = devlink_nl_health_reporter_fill(msg, devlink, reporter,
+                                             DEVLINK_CMD_HEALTH_REPORTER_GET,
+                                             info->snd_portid, info->snd_seq,
+                                             0);
+       if (err) {
+               nlmsg_free(msg);
+               return err;
+       }
+
+       return genlmsg_reply(msg, info);
+}
+
+static int
+devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
+                                         struct netlink_callback *cb)
+{
+       struct devlink_health_reporter *reporter;
+       struct devlink *devlink;
+       int start = cb->args[0];
+       int idx = 0;
+       int err;
+
+       mutex_lock(&devlink_mutex);
+       list_for_each_entry(devlink, &devlink_list, list) {
+               if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
+                       continue;
+               mutex_lock(&devlink->lock);
+               list_for_each_entry(reporter, &devlink->reporter_list,
+                                   list) {
+                       if (idx < start) {
+                               idx++;
+                               continue;
+                       }
+                       err = devlink_nl_health_reporter_fill(msg, devlink,
+                                                             reporter,
+                                                             DEVLINK_CMD_HEALTH_REPORTER_GET,
+                                                             NETLINK_CB(cb->skb).portid,
+                                                             cb->nlh->nlmsg_seq,
+                                                             NLM_F_MULTI);
+                       if (err) {
+                               mutex_unlock(&devlink->lock);
+                               goto out;
+                       }
+                       idx++;
+               }
+               mutex_unlock(&devlink->lock);
+       }
+out:
+       mutex_unlock(&devlink_mutex);
+
+       cb->args[0] = idx;
+       return msg->len;
+}
+
+static int
+devlink_nl_cmd_health_reporter_set_doit(struct sk_buff *skb,
+                                       struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       if (!reporter->ops->recover &&
+           (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] ||
+            info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]))
+               return -EOPNOTSUPP;
+
+       if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
+               reporter->graceful_period =
+                       nla_get_u64(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]);
+
+       if (info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
+               reporter->auto_recover =
+                       nla_get_u8(info->attrs[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]);
+
+       return 0;
+}
+
+static int devlink_nl_cmd_health_reporter_recover_doit(struct sk_buff *skb,
+                                                      struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       return devlink_health_reporter_recover(reporter, NULL);
+}
+
+static int devlink_nl_cmd_health_reporter_diagnose_doit(struct sk_buff *skb,
+                                                       struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+       struct devlink_fmsg *fmsg;
+       int err;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       if (!reporter->ops->diagnose)
+               return -EOPNOTSUPP;
+
+       fmsg = devlink_fmsg_alloc();
+       if (!fmsg)
+               return -ENOMEM;
+
+       err = devlink_fmsg_obj_nest_start(fmsg);
+       if (err)
+               goto out;
+
+       err = reporter->ops->diagnose(reporter, fmsg);
+       if (err)
+               goto out;
+
+       err = devlink_fmsg_obj_nest_end(fmsg);
+       if (err)
+               goto out;
+
+       err = devlink_fmsg_snd(fmsg, info,
+                              DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, 0);
+
+out:
+       devlink_fmsg_free(fmsg);
+       return err;
+}
+
+static int devlink_nl_cmd_health_reporter_dump_get_doit(struct sk_buff *skb,
+                                                       struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+       int err;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       if (!reporter->ops->dump)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&reporter->dump_lock);
+       err = devlink_health_do_dump(reporter, NULL);
+       if (err)
+               goto out;
+
+       err = devlink_fmsg_snd(reporter->dump_fmsg, info,
+                              DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET, 0);
+
+out:
+       mutex_unlock(&reporter->dump_lock);
+       return err;
+}
+
+static int
+devlink_nl_cmd_health_reporter_dump_clear_doit(struct sk_buff *skb,
+                                              struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       struct devlink_health_reporter *reporter;
+
+       reporter = devlink_health_reporter_get_from_info(devlink, info);
+       if (!reporter)
+               return -EINVAL;
+
+       if (!reporter->ops->dump)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&reporter->dump_lock);
+       devlink_health_dump_clear(reporter);
+       mutex_unlock(&reporter->dump_lock);
+       return 0;
+}
+
+static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
+       [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_PORT_TYPE] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_PORT_SPLIT_COUNT] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_SB_INDEX] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_SB_POOL_INDEX] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_SB_POOL_TYPE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_SB_POOL_SIZE] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_SB_THRESHOLD] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_ESWITCH_MODE] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_RESOURCE_ID] = { .type = NLA_U64},
+       [DEVLINK_ATTR_RESOURCE_SIZE] = { .type = NLA_U64},
+       [DEVLINK_ATTR_PARAM_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_PARAM_TYPE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_PARAM_VALUE_CMODE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_REGION_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_REGION_SNAPSHOT_ID] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_HEALTH_REPORTER_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = { .type = NLA_U64 },
+       [DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = { .type = NLA_NUL_STRING },
+};
+
+static const struct genl_ops devlink_nl_ops[] = {
+       {
+               .cmd = DEVLINK_CMD_GET,
+               .doit = devlink_nl_cmd_get_doit,
+               .dumpit = devlink_nl_cmd_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_PORT_GET,
+               .doit = devlink_nl_cmd_port_get_doit,
+               .dumpit = devlink_nl_cmd_port_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_PORT_SET,
+               .doit = devlink_nl_cmd_port_set_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
+       },
        {
                .cmd = DEVLINK_CMD_PORT_SPLIT,
                .doit = devlink_nl_cmd_port_split_doit,
@@ -3821,23 +5105,98 @@ static const struct genl_ops devlink_nl_ops[] = {
                .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
        },
        {
+               .cmd = DEVLINK_CMD_PORT_PARAM_GET,
+               .doit = devlink_nl_cmd_port_param_get_doit,
+               .dumpit = devlink_nl_cmd_port_param_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_PORT_PARAM_SET,
+               .doit = devlink_nl_cmd_port_param_set_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
+       },
+       {
                .cmd = DEVLINK_CMD_REGION_GET,
                .doit = devlink_nl_cmd_region_get_doit,
                .dumpit = devlink_nl_cmd_region_get_dumpit,
                .policy = devlink_nl_policy,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
+               .cmd = DEVLINK_CMD_REGION_DEL,
+               .doit = devlink_nl_cmd_region_del,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
+               .cmd = DEVLINK_CMD_REGION_READ,
+               .dumpit = devlink_nl_cmd_region_read_dumpit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
+               .cmd = DEVLINK_CMD_INFO_GET,
+               .doit = devlink_nl_cmd_info_get_doit,
+               .dumpit = devlink_nl_cmd_info_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_GET,
+               .doit = devlink_nl_cmd_health_reporter_get_doit,
+               .dumpit = devlink_nl_cmd_health_reporter_get_dumpit,
+               .policy = devlink_nl_policy,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_SET,
+               .doit = devlink_nl_cmd_health_reporter_set_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_RECOVER,
+               .doit = devlink_nl_cmd_health_reporter_recover_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE,
+               .doit = devlink_nl_cmd_health_reporter_diagnose_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
+       {
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
+               .doit = devlink_nl_cmd_health_reporter_dump_get_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
-               .cmd = DEVLINK_CMD_REGION_DEL,
-               .doit = devlink_nl_cmd_region_del,
+               .cmd = DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR,
+               .doit = devlink_nl_cmd_health_reporter_dump_clear_doit,
                .policy = devlink_nl_policy,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
-               .cmd = DEVLINK_CMD_REGION_READ,
-               .dumpit = devlink_nl_cmd_region_read_dumpit,
+               .cmd = DEVLINK_CMD_FLASH_UPDATE,
+               .doit = devlink_nl_cmd_flash_update,
                .policy = devlink_nl_policy,
                .flags = GENL_ADMIN_PERM,
                .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
@@ -3882,6 +5241,7 @@ struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size)
        INIT_LIST_HEAD(&devlink->resource_list);
        INIT_LIST_HEAD(&devlink->param_list);
        INIT_LIST_HEAD(&devlink->region_list);
+       INIT_LIST_HEAD(&devlink->reporter_list);
        mutex_init(&devlink->lock);
        return devlink;
 }
@@ -3924,6 +5284,14 @@ EXPORT_SYMBOL_GPL(devlink_unregister);
  */
 void devlink_free(struct devlink *devlink)
 {
+       WARN_ON(!list_empty(&devlink->reporter_list));
+       WARN_ON(!list_empty(&devlink->region_list));
+       WARN_ON(!list_empty(&devlink->param_list));
+       WARN_ON(!list_empty(&devlink->resource_list));
+       WARN_ON(!list_empty(&devlink->dpipe_table_list));
+       WARN_ON(!list_empty(&devlink->sb_list));
+       WARN_ON(!list_empty(&devlink->port_list));
+
        kfree(devlink);
 }
 EXPORT_SYMBOL_GPL(devlink_free);
@@ -3954,6 +5322,7 @@ int devlink_port_register(struct devlink *devlink,
        devlink_port->index = port_index;
        devlink_port->registered = true;
        list_add_tail(&devlink_port->list, &devlink->port_list);
+       INIT_LIST_HEAD(&devlink_port->param_list);
        mutex_unlock(&devlink->lock);
        devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
        return 0;
@@ -4471,18 +5840,23 @@ out:
 }
 EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);
 
-/**
- *     devlink_params_register - register configuration parameters
- *
- *     @devlink: devlink
- *     @params: configuration parameters array
- *     @params_count: number of parameters provided
- *
- *     Register the configuration parameters supported by the driver.
- */
-int devlink_params_register(struct devlink *devlink,
-                           const struct devlink_param *params,
-                           size_t params_count)
+static int devlink_param_verify(const struct devlink_param *param)
+{
+       if (!param || !param->name || !param->supported_cmodes)
+               return -EINVAL;
+       if (param->generic)
+               return devlink_param_generic_verify(param);
+       else
+               return devlink_param_driver_verify(param);
+}
+
+static int __devlink_params_register(struct devlink *devlink,
+                                    unsigned int port_index,
+                                    struct list_head *param_list,
+                                    const struct devlink_param *params,
+                                    size_t params_count,
+                                    enum devlink_command reg_cmd,
+                                    enum devlink_command unreg_cmd)
 {
        const struct devlink_param *param = params;
        int i;
@@ -4490,20 +5864,12 @@ int devlink_params_register(struct devlink *devlink,
 
        mutex_lock(&devlink->lock);
        for (i = 0; i < params_count; i++, param++) {
-               if (!param || !param->name || !param->supported_cmodes) {
-                       err = -EINVAL;
+               err = devlink_param_verify(param);
+               if (err)
                        goto rollback;
-               }
-               if (param->generic) {
-                       err = devlink_param_generic_verify(param);
-                       if (err)
-                               goto rollback;
-               } else {
-                       err = devlink_param_driver_verify(param);
-                       if (err)
-                               goto rollback;
-               }
-               err = devlink_param_register_one(devlink, param);
+
+               err = devlink_param_register_one(devlink, port_index,
+                                                param_list, param, reg_cmd);
                if (err)
                        goto rollback;
        }
@@ -4515,11 +5881,48 @@ rollback:
        if (!i)
                goto unlock;
        for (param--; i > 0; i--, param--)
-               devlink_param_unregister_one(devlink, param);
+               devlink_param_unregister_one(devlink, port_index, param_list,
+                                            param, unreg_cmd);
 unlock:
        mutex_unlock(&devlink->lock);
        return err;
 }
+
+static void __devlink_params_unregister(struct devlink *devlink,
+                                       unsigned int port_index,
+                                       struct list_head *param_list,
+                                       const struct devlink_param *params,
+                                       size_t params_count,
+                                       enum devlink_command cmd)
+{
+       const struct devlink_param *param = params;
+       int i;
+
+       mutex_lock(&devlink->lock);
+       for (i = 0; i < params_count; i++, param++)
+               devlink_param_unregister_one(devlink, 0, param_list, param,
+                                            cmd);
+       mutex_unlock(&devlink->lock);
+}
+
+/**
+ *     devlink_params_register - register configuration parameters
+ *
+ *     @devlink: devlink
+ *     @params: configuration parameters array
+ *     @params_count: number of parameters provided
+ *
+ *     Register the configuration parameters supported by the driver.
+ */
+int devlink_params_register(struct devlink *devlink,
+                           const struct devlink_param *params,
+                           size_t params_count)
+{
+       return __devlink_params_register(devlink, 0, &devlink->param_list,
+                                        params, params_count,
+                                        DEVLINK_CMD_PARAM_NEW,
+                                        DEVLINK_CMD_PARAM_DEL);
+}
 EXPORT_SYMBOL_GPL(devlink_params_register);
 
 /**
@@ -4532,36 +5935,103 @@ void devlink_params_unregister(struct devlink *devlink,
                               const struct devlink_param *params,
                               size_t params_count)
 {
-       const struct devlink_param *param = params;
-       int i;
-
-       mutex_lock(&devlink->lock);
-       for (i = 0; i < params_count; i++, param++)
-               devlink_param_unregister_one(devlink, param);
-       mutex_unlock(&devlink->lock);
+       return __devlink_params_unregister(devlink, 0, &devlink->param_list,
+                                          params, params_count,
+                                          DEVLINK_CMD_PARAM_DEL);
 }
 EXPORT_SYMBOL_GPL(devlink_params_unregister);
 
 /**
- *     devlink_param_driverinit_value_get - get configuration parameter
- *                                          value for driver initializing
+ *     devlink_params_publish - publish configuration parameters
  *
  *     @devlink: devlink
- *     @param_id: parameter ID
- *     @init_val: value of parameter in driverinit configuration mode
  *
- *     This function should be used by the driver to get driverinit
- *     configuration for initialization after reload command.
+ *     Publish previously registered configuration parameters.
  */
-int devlink_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
-                                      union devlink_param_value *init_val)
+void devlink_params_publish(struct devlink *devlink)
 {
        struct devlink_param_item *param_item;
 
-       if (!devlink->ops || !devlink->ops->reload)
-               return -EOPNOTSUPP;
+       list_for_each_entry(param_item, &devlink->param_list, list) {
+               if (param_item->published)
+                       continue;
+               param_item->published = true;
+               devlink_param_notify(devlink, 0, param_item,
+                                    DEVLINK_CMD_PARAM_NEW);
+       }
+}
+EXPORT_SYMBOL_GPL(devlink_params_publish);
 
-       param_item = devlink_param_find_by_id(&devlink->param_list, param_id);
+/**
+ *     devlink_params_unpublish - unpublish configuration parameters
+ *
+ *     @devlink: devlink
+ *
+ *     Unpublish previously registered configuration parameters.
+ */
+void devlink_params_unpublish(struct devlink *devlink)
+{
+       struct devlink_param_item *param_item;
+
+       list_for_each_entry(param_item, &devlink->param_list, list) {
+               if (!param_item->published)
+                       continue;
+               param_item->published = false;
+               devlink_param_notify(devlink, 0, param_item,
+                                    DEVLINK_CMD_PARAM_DEL);
+       }
+}
+EXPORT_SYMBOL_GPL(devlink_params_unpublish);
+
+/**
+ *     devlink_port_params_register - register port configuration parameters
+ *
+ *     @devlink_port: devlink port
+ *     @params: configuration parameters array
+ *     @params_count: number of parameters provided
+ *
+ *     Register the configuration parameters supported by the port.
+ */
+int devlink_port_params_register(struct devlink_port *devlink_port,
+                                const struct devlink_param *params,
+                                size_t params_count)
+{
+       return __devlink_params_register(devlink_port->devlink,
+                                        devlink_port->index,
+                                        &devlink_port->param_list, params,
+                                        params_count,
+                                        DEVLINK_CMD_PORT_PARAM_NEW,
+                                        DEVLINK_CMD_PORT_PARAM_DEL);
+}
+EXPORT_SYMBOL_GPL(devlink_port_params_register);
+
+/**
+ *     devlink_port_params_unregister - unregister port configuration
+ *     parameters
+ *
+ *     @devlink_port: devlink port
+ *     @params: configuration parameters array
+ *     @params_count: number of parameters provided
+ */
+void devlink_port_params_unregister(struct devlink_port *devlink_port,
+                                   const struct devlink_param *params,
+                                   size_t params_count)
+{
+       return __devlink_params_unregister(devlink_port->devlink,
+                                          devlink_port->index,
+                                          &devlink_port->param_list,
+                                          params, params_count,
+                                          DEVLINK_CMD_PORT_PARAM_DEL);
+}
+EXPORT_SYMBOL_GPL(devlink_port_params_unregister);
+
+static int
+__devlink_param_driverinit_value_get(struct list_head *param_list, u32 param_id,
+                                    union devlink_param_value *init_val)
+{
+       struct devlink_param_item *param_item;
+
+       param_item = devlink_param_find_by_id(param_list, param_id);
        if (!param_item)
                return -EINVAL;
 
@@ -4577,6 +6047,54 @@ int devlink_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
 
        return 0;
 }
+
+static int
+__devlink_param_driverinit_value_set(struct devlink *devlink,
+                                    unsigned int port_index,
+                                    struct list_head *param_list, u32 param_id,
+                                    union devlink_param_value init_val,
+                                    enum devlink_command cmd)
+{
+       struct devlink_param_item *param_item;
+
+       param_item = devlink_param_find_by_id(param_list, param_id);
+       if (!param_item)
+               return -EINVAL;
+
+       if (!devlink_param_cmode_is_supported(param_item->param,
+                                             DEVLINK_PARAM_CMODE_DRIVERINIT))
+               return -EOPNOTSUPP;
+
+       if (param_item->param->type == DEVLINK_PARAM_TYPE_STRING)
+               strcpy(param_item->driverinit_value.vstr, init_val.vstr);
+       else
+               param_item->driverinit_value = init_val;
+       param_item->driverinit_value_valid = true;
+
+       devlink_param_notify(devlink, port_index, param_item, cmd);
+       return 0;
+}
+
+/**
+ *     devlink_param_driverinit_value_get - get configuration parameter
+ *                                          value for driver initializing
+ *
+ *     @devlink: devlink
+ *     @param_id: parameter ID
+ *     @init_val: value of parameter in driverinit configuration mode
+ *
+ *     This function should be used by the driver to get driverinit
+ *     configuration for initialization after reload command.
+ */
+int devlink_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
+                                      union devlink_param_value *init_val)
+{
+       if (!devlink->ops || !devlink->ops->reload)
+               return -EOPNOTSUPP;
+
+       return __devlink_param_driverinit_value_get(&devlink->param_list,
+                                                   param_id, init_val);
+}
 EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_get);
 
 /**
@@ -4594,26 +6112,61 @@ EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_get);
 int devlink_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
                                       union devlink_param_value init_val)
 {
-       struct devlink_param_item *param_item;
+       return __devlink_param_driverinit_value_set(devlink, 0,
+                                                   &devlink->param_list,
+                                                   param_id, init_val,
+                                                   DEVLINK_CMD_PARAM_NEW);
+}
+EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_set);
 
-       param_item = devlink_param_find_by_id(&devlink->param_list, param_id);
-       if (!param_item)
-               return -EINVAL;
+/**
+ *     devlink_port_param_driverinit_value_get - get configuration parameter
+ *                                             value for driver initializing
+ *
+ *     @devlink_port: devlink_port
+ *     @param_id: parameter ID
+ *     @init_val: value of parameter in driverinit configuration mode
+ *
+ *     This function should be used by the driver to get driverinit
+ *     configuration for initialization after reload command.
+ */
+int devlink_port_param_driverinit_value_get(struct devlink_port *devlink_port,
+                                           u32 param_id,
+                                           union devlink_param_value *init_val)
+{
+       struct devlink *devlink = devlink_port->devlink;
 
-       if (!devlink_param_cmode_is_supported(param_item->param,
-                                             DEVLINK_PARAM_CMODE_DRIVERINIT))
+       if (!devlink->ops || !devlink->ops->reload)
                return -EOPNOTSUPP;
 
-       if (param_item->param->type == DEVLINK_PARAM_TYPE_STRING)
-               strcpy(param_item->driverinit_value.vstr, init_val.vstr);
-       else
-               param_item->driverinit_value = init_val;
-       param_item->driverinit_value_valid = true;
+       return __devlink_param_driverinit_value_get(&devlink_port->param_list,
+                                                   param_id, init_val);
+}
+EXPORT_SYMBOL_GPL(devlink_port_param_driverinit_value_get);
 
-       devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
-       return 0;
+/**
+ *     devlink_port_param_driverinit_value_set - set value of configuration
+ *                                               parameter for driverinit
+ *                                               configuration mode
+ *
+ *     @devlink_port: devlink_port
+ *     @param_id: parameter ID
+ *     @init_val: value of parameter to set for driverinit configuration mode
+ *
+ *     This function should be used by the driver to set driverinit
+ *     configuration mode default value.
+ */
+int devlink_port_param_driverinit_value_set(struct devlink_port *devlink_port,
+                                           u32 param_id,
+                                           union devlink_param_value init_val)
+{
+       return __devlink_param_driverinit_value_set(devlink_port->devlink,
+                                                   devlink_port->index,
+                                                   &devlink_port->param_list,
+                                                   param_id, init_val,
+                                                   DEVLINK_CMD_PORT_PARAM_NEW);
 }
-EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_set);
+EXPORT_SYMBOL_GPL(devlink_port_param_driverinit_value_set);
 
 /**
  *     devlink_param_value_changed - notify devlink on a parameter's value
@@ -4626,7 +6179,6 @@ EXPORT_SYMBOL_GPL(devlink_param_driverinit_value_set);
  *     This function should be used by the driver to notify devlink on value
  *     change, excluding driverinit configuration mode.
  *     For driverinit configuration mode driver should use the function
- *     devlink_param_driverinit_value_set() instead.
  */
 void devlink_param_value_changed(struct devlink *devlink, u32 param_id)
 {
@@ -4635,11 +6187,38 @@ void devlink_param_value_changed(struct devlink *devlink, u32 param_id)
        param_item = devlink_param_find_by_id(&devlink->param_list, param_id);
        WARN_ON(!param_item);
 
-       devlink_param_notify(devlink, param_item, DEVLINK_CMD_PARAM_NEW);
+       devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
 }
 EXPORT_SYMBOL_GPL(devlink_param_value_changed);
 
 /**
+ *     devlink_port_param_value_changed - notify devlink on a parameter's value
+ *                                      change. Should be called by the driver
+ *                                      right after the change.
+ *
+ *     @devlink_port: devlink_port
+ *     @param_id: parameter ID
+ *
+ *     This function should be used by the driver to notify devlink on value
+ *     change, excluding driverinit configuration mode.
+ *     For driverinit configuration mode driver should use the function
+ *     devlink_port_param_driverinit_value_set() instead.
+ */
+void devlink_port_param_value_changed(struct devlink_port *devlink_port,
+                                     u32 param_id)
+{
+       struct devlink_param_item *param_item;
+
+       param_item = devlink_param_find_by_id(&devlink_port->param_list,
+                                             param_id);
+       WARN_ON(!param_item);
+
+       devlink_param_notify(devlink_port->devlink, devlink_port->index,
+                            param_item, DEVLINK_CMD_PORT_PARAM_NEW);
+}
+EXPORT_SYMBOL_GPL(devlink_port_param_value_changed);
+
+/**
  *     devlink_param_value_str_fill - Safely fill-up the string preventing
  *                                    from overflow of the preallocated buffer
  *
@@ -4808,6 +6387,99 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(devlink_region_snapshot_create);
 
+static void __devlink_compat_running_version(struct devlink *devlink,
+                                            char *buf, size_t len)
+{
+       const struct nlattr *nlattr;
+       struct devlink_info_req req;
+       struct sk_buff *msg;
+       int rem, err;
+
+       if (!devlink->ops->info_get)
+               return;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       req.msg = msg;
+       err = devlink->ops->info_get(devlink, &req, NULL);
+       if (err)
+               goto free_msg;
+
+       nla_for_each_attr(nlattr, (void *)msg->data, msg->len, rem) {
+               const struct nlattr *kv;
+               int rem_kv;
+
+               if (nla_type(nlattr) != DEVLINK_ATTR_INFO_VERSION_RUNNING)
+                       continue;
+
+               nla_for_each_nested(kv, nlattr, rem_kv) {
+                       if (nla_type(kv) != DEVLINK_ATTR_INFO_VERSION_VALUE)
+                               continue;
+
+                       strlcat(buf, nla_data(kv), len);
+                       strlcat(buf, " ", len);
+               }
+       }
+free_msg:
+       nlmsg_free(msg);
+}
+
+void devlink_compat_running_version(struct net_device *dev,
+                                   char *buf, size_t len)
+{
+       struct devlink_port *devlink_port;
+       struct devlink *devlink;
+
+       mutex_lock(&devlink_mutex);
+       list_for_each_entry(devlink, &devlink_list, list) {
+               mutex_lock(&devlink->lock);
+               list_for_each_entry(devlink_port, &devlink->port_list, list) {
+                       if (devlink_port->type == DEVLINK_PORT_TYPE_ETH &&
+                           devlink_port->type_dev == dev) {
+                               __devlink_compat_running_version(devlink,
+                                                                buf, len);
+                               mutex_unlock(&devlink->lock);
+                               goto out;
+                       }
+               }
+               mutex_unlock(&devlink->lock);
+       }
+out:
+       mutex_unlock(&devlink_mutex);
+}
+
+int devlink_compat_flash_update(struct net_device *dev, const char *file_name)
+{
+       struct devlink_port *devlink_port;
+       struct devlink *devlink;
+
+       mutex_lock(&devlink_mutex);
+       list_for_each_entry(devlink, &devlink_list, list) {
+               mutex_lock(&devlink->lock);
+               list_for_each_entry(devlink_port, &devlink->port_list, list) {
+                       int ret = -EOPNOTSUPP;
+
+                       if (devlink_port->type != DEVLINK_PORT_TYPE_ETH ||
+                           devlink_port->type_dev != dev)
+                               continue;
+
+                       mutex_unlock(&devlink_mutex);
+                       if (devlink->ops->flash_update)
+                               ret = devlink->ops->flash_update(devlink,
+                                                                file_name,
+                                                                NULL, NULL);
+                       mutex_unlock(&devlink->lock);
+                       return ret;
+               }
+               mutex_unlock(&devlink->lock);
+       }
+       mutex_unlock(&devlink_mutex);
+
+       return -EOPNOTSUPP;
+}
+
 static int __init devlink_module_init(void)
 {
        return genl_register_family(&devlink_nl_family);
index 158264f..1320e8d 100644 (file)
@@ -27,7 +27,9 @@
 #include <linux/rtnetlink.h>
 #include <linux/sched/signal.h>
 #include <linux/net.h>
+#include <net/devlink.h>
 #include <net/xdp_sock.h>
+#include <net/flow_offload.h>
 
 /*
  * Some useful ethtool_ops methods that're device independent.
@@ -803,6 +805,12 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
        if (ops->get_eeprom_len)
                info.eedump_len = ops->get_eeprom_len(dev);
 
+       rtnl_unlock();
+       if (!info.fw_version[0])
+               devlink_compat_running_version(dev, info.fw_version,
+                                              sizeof(info.fw_version));
+       rtnl_lock();
+
        if (copy_to_user(useraddr, &info, sizeof(info)))
                return -EFAULT;
        return 0;
@@ -1348,12 +1356,9 @@ static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
        if (regs.len > reglen)
                regs.len = reglen;
 
-       regbuf = NULL;
-       if (reglen) {
-               regbuf = vzalloc(reglen);
-               if (!regbuf)
-                       return -ENOMEM;
-       }
+       regbuf = vzalloc(reglen);
+       if (!regbuf)
+               return -ENOMEM;
 
        ops->get_regs(dev, &regs, regbuf);
 
@@ -2033,11 +2038,17 @@ static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
 
        if (copy_from_user(&efl, useraddr, sizeof(efl)))
                return -EFAULT;
+       efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
 
-       if (!dev->ethtool_ops->flash_device)
-               return -EOPNOTSUPP;
+       if (!dev->ethtool_ops->flash_device) {
+               int ret;
 
-       efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
+               rtnl_unlock();
+               ret = devlink_compat_flash_update(dev, efl.data);
+               rtnl_lock();
+
+               return ret;
+       }
 
        return dev->ethtool_ops->flash_device(dev, &efl);
 }
@@ -2816,3 +2827,241 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
 
        return rc;
 }
+
+struct ethtool_rx_flow_key {
+       struct flow_dissector_key_basic                 basic;
+       union {
+               struct flow_dissector_key_ipv4_addrs    ipv4;
+               struct flow_dissector_key_ipv6_addrs    ipv6;
+       };
+       struct flow_dissector_key_ports                 tp;
+       struct flow_dissector_key_ip                    ip;
+       struct flow_dissector_key_vlan                  vlan;
+       struct flow_dissector_key_eth_addrs             eth_addrs;
+} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
+
+struct ethtool_rx_flow_match {
+       struct flow_dissector           dissector;
+       struct ethtool_rx_flow_key      key;
+       struct ethtool_rx_flow_key      mask;
+};
+
+struct ethtool_rx_flow_rule *
+ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input)
+{
+       const struct ethtool_rx_flow_spec *fs = input->fs;
+       static struct in6_addr zero_addr = {};
+       struct ethtool_rx_flow_match *match;
+       struct ethtool_rx_flow_rule *flow;
+       struct flow_action_entry *act;
+
+       flow = kzalloc(sizeof(struct ethtool_rx_flow_rule) +
+                      sizeof(struct ethtool_rx_flow_match), GFP_KERNEL);
+       if (!flow)
+               return ERR_PTR(-ENOMEM);
+
+       /* ethtool_rx supports only one single action per rule. */
+       flow->rule = flow_rule_alloc(1);
+       if (!flow->rule) {
+               kfree(flow);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       match = (struct ethtool_rx_flow_match *)flow->priv;
+       flow->rule->match.dissector     = &match->dissector;
+       flow->rule->match.mask          = &match->mask;
+       flow->rule->match.key           = &match->key;
+
+       match->mask.basic.n_proto = htons(0xffff);
+
+       switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
+       case TCP_V4_FLOW:
+       case UDP_V4_FLOW: {
+               const struct ethtool_tcpip4_spec *v4_spec, *v4_m_spec;
+
+               match->key.basic.n_proto = htons(ETH_P_IP);
+
+               v4_spec = &fs->h_u.tcp_ip4_spec;
+               v4_m_spec = &fs->m_u.tcp_ip4_spec;
+
+               if (v4_m_spec->ip4src) {
+                       match->key.ipv4.src = v4_spec->ip4src;
+                       match->mask.ipv4.src = v4_m_spec->ip4src;
+               }
+               if (v4_m_spec->ip4dst) {
+                       match->key.ipv4.dst = v4_spec->ip4dst;
+                       match->mask.ipv4.dst = v4_m_spec->ip4dst;
+               }
+               if (v4_m_spec->ip4src ||
+                   v4_m_spec->ip4dst) {
+                       match->dissector.used_keys |=
+                               BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS);
+                       match->dissector.offset[FLOW_DISSECTOR_KEY_IPV4_ADDRS] =
+                               offsetof(struct ethtool_rx_flow_key, ipv4);
+               }
+               if (v4_m_spec->psrc) {
+                       match->key.tp.src = v4_spec->psrc;
+                       match->mask.tp.src = v4_m_spec->psrc;
+               }
+               if (v4_m_spec->pdst) {
+                       match->key.tp.dst = v4_spec->pdst;
+                       match->mask.tp.dst = v4_m_spec->pdst;
+               }
+               if (v4_m_spec->psrc ||
+                   v4_m_spec->pdst) {
+                       match->dissector.used_keys |=
+                               BIT(FLOW_DISSECTOR_KEY_PORTS);
+                       match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
+                               offsetof(struct ethtool_rx_flow_key, tp);
+               }
+               if (v4_m_spec->tos) {
+                       match->key.ip.tos = v4_spec->tos;
+                       match->mask.ip.tos = v4_m_spec->tos;
+                       match->dissector.used_keys |=
+                               BIT(FLOW_DISSECTOR_KEY_IP);
+                       match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
+                               offsetof(struct ethtool_rx_flow_key, ip);
+               }
+               }
+               break;
+       case TCP_V6_FLOW:
+       case UDP_V6_FLOW: {
+               const struct ethtool_tcpip6_spec *v6_spec, *v6_m_spec;
+
+               match->key.basic.n_proto = htons(ETH_P_IPV6);
+
+               v6_spec = &fs->h_u.tcp_ip6_spec;
+               v6_m_spec = &fs->m_u.tcp_ip6_spec;
+               if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr))) {
+                       memcpy(&match->key.ipv6.src, v6_spec->ip6src,
+                              sizeof(match->key.ipv6.src));
+                       memcpy(&match->mask.ipv6.src, v6_m_spec->ip6src,
+                              sizeof(match->mask.ipv6.src));
+               }
+               if (memcmp(v6_m_spec->ip6dst, &zero_addr, sizeof(zero_addr))) {
+                       memcpy(&match->key.ipv6.dst, v6_spec->ip6dst,
+                              sizeof(match->key.ipv6.dst));
+                       memcpy(&match->mask.ipv6.dst, v6_m_spec->ip6dst,
+                              sizeof(match->mask.ipv6.dst));
+               }
+               if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr)) ||
+                   memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr))) {
+                       match->dissector.used_keys |=
+                               BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS);
+                       match->dissector.offset[FLOW_DISSECTOR_KEY_IPV6_ADDRS] =
+                               offsetof(struct ethtool_rx_flow_key, ipv6);
+               }
+               if (v6_m_spec->psrc) {
+                       match->key.tp.src = v6_spec->psrc;
+                       match->mask.tp.src = v6_m_spec->psrc;
+               }
+               if (v6_m_spec->pdst) {
+                       match->key.tp.dst = v6_spec->pdst;
+                       match->mask.tp.dst = v6_m_spec->pdst;
+               }
+               if (v6_m_spec->psrc ||
+                   v6_m_spec->pdst) {
+                       match->dissector.used_keys |=
+                               BIT(FLOW_DISSECTOR_KEY_PORTS);
+                       match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
+                               offsetof(struct ethtool_rx_flow_key, tp);
+               }
+               if (v6_m_spec->tclass) {
+                       match->key.ip.tos = v6_spec->tclass;
+                       match->mask.ip.tos = v6_m_spec->tclass;
+                       match->dissector.used_keys |=
+                               BIT(FLOW_DISSECTOR_KEY_IP);
+                       match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
+                               offsetof(struct ethtool_rx_flow_key, ip);
+               }
+               }
+               break;
+       default:
+               ethtool_rx_flow_rule_destroy(flow);
+               return ERR_PTR(-EINVAL);
+       }
+
+       switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
+       case TCP_V4_FLOW:
+       case TCP_V6_FLOW:
+               match->key.basic.ip_proto = IPPROTO_TCP;
+               break;
+       case UDP_V4_FLOW:
+       case UDP_V6_FLOW:
+               match->key.basic.ip_proto = IPPROTO_UDP;
+               break;
+       }
+       match->mask.basic.ip_proto = 0xff;
+
+       match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
+       match->dissector.offset[FLOW_DISSECTOR_KEY_BASIC] =
+               offsetof(struct ethtool_rx_flow_key, basic);
+
+       if (fs->flow_type & FLOW_EXT) {
+               const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext;
+               const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext;
+
+               if (ext_m_spec->vlan_etype &&
+                   ext_m_spec->vlan_tci) {
+                       match->key.vlan.vlan_tpid = ext_h_spec->vlan_etype;
+                       match->mask.vlan.vlan_tpid = ext_m_spec->vlan_etype;
+
+                       match->key.vlan.vlan_id =
+                               ntohs(ext_h_spec->vlan_tci) & 0x0fff;
+                       match->mask.vlan.vlan_id =
+                               ntohs(ext_m_spec->vlan_tci) & 0x0fff;
+
+                       match->key.vlan.vlan_priority =
+                               (ntohs(ext_h_spec->vlan_tci) & 0xe000) >> 13;
+                       match->mask.vlan.vlan_priority =
+                               (ntohs(ext_m_spec->vlan_tci) & 0xe000) >> 13;
+
+                       match->dissector.used_keys |=
+                               BIT(FLOW_DISSECTOR_KEY_VLAN);
+                       match->dissector.offset[FLOW_DISSECTOR_KEY_VLAN] =
+                               offsetof(struct ethtool_rx_flow_key, vlan);
+               }
+       }
+       if (fs->flow_type & FLOW_MAC_EXT) {
+               const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext;
+               const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext;
+
+               memcpy(match->key.eth_addrs.dst, ext_h_spec->h_dest,
+                      ETH_ALEN);
+               memcpy(match->mask.eth_addrs.dst, ext_m_spec->h_dest,
+                      ETH_ALEN);
+
+               match->dissector.used_keys |=
+                       BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
+               match->dissector.offset[FLOW_DISSECTOR_KEY_ETH_ADDRS] =
+                       offsetof(struct ethtool_rx_flow_key, eth_addrs);
+       }
+
+       act = &flow->rule->action.entries[0];
+       switch (fs->ring_cookie) {
+       case RX_CLS_FLOW_DISC:
+               act->id = FLOW_ACTION_DROP;
+               break;
+       case RX_CLS_FLOW_WAKE:
+               act->id = FLOW_ACTION_WAKE;
+               break;
+       default:
+               act->id = FLOW_ACTION_QUEUE;
+               if (fs->flow_type & FLOW_RSS)
+                       act->queue.ctx = input->rss_ctx;
+
+               act->queue.vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie);
+               act->queue.index = ethtool_get_flow_spec_ring(fs->ring_cookie);
+               break;
+       }
+
+       return flow;
+}
+EXPORT_SYMBOL(ethtool_rx_flow_rule_create);
+
+void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *flow)
+{
+       kfree(flow->rule);
+       kfree(flow);
+}
+EXPORT_SYMBOL(ethtool_rx_flow_rule_destroy);
index 41984ad..b584cb4 100644 (file)
@@ -73,6 +73,7 @@
 #include <linux/seg6_local.h>
 #include <net/seg6.h>
 #include <net/seg6_local.h>
+#include <net/lwtunnel.h>
 
 /**
  *     sk_filter_trim_cap - run a packet through a socket filter
@@ -1793,6 +1794,20 @@ static const struct bpf_func_proto bpf_skb_pull_data_proto = {
        .arg2_type      = ARG_ANYTHING,
 };
 
+BPF_CALL_1(bpf_sk_fullsock, struct sock *, sk)
+{
+       sk = sk_to_full_sk(sk);
+
+       return sk_fullsock(sk) ? (unsigned long)sk : (unsigned long)NULL;
+}
+
+static const struct bpf_func_proto bpf_sk_fullsock_proto = {
+       .func           = bpf_sk_fullsock,
+       .gpl_only       = false,
+       .ret_type       = RET_PTR_TO_SOCKET_OR_NULL,
+       .arg1_type      = ARG_PTR_TO_SOCK_COMMON,
+};
+
 static inline int sk_skb_try_make_writable(struct sk_buff *skb,
                                           unsigned int write_len)
 {
@@ -4112,10 +4127,12 @@ BPF_CALL_5(bpf_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
                /* Only some socketops are supported */
                switch (optname) {
                case SO_RCVBUF:
+                       val = min_t(u32, val, sysctl_rmem_max);
                        sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
                        sk->sk_rcvbuf = max_t(int, val * 2, SOCK_MIN_RCVBUF);
                        break;
                case SO_SNDBUF:
+                       val = min_t(u32, val, sysctl_wmem_max);
                        sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
                        sk->sk_sndbuf = max_t(int, val * 2, SOCK_MIN_SNDBUF);
                        break;
@@ -4801,7 +4818,15 @@ static int bpf_push_seg6_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len
 }
 #endif /* CONFIG_IPV6_SEG6_BPF */
 
-BPF_CALL_4(bpf_lwt_push_encap, struct sk_buff *, skb, u32, type, void *, hdr,
+#if IS_ENABLED(CONFIG_LWTUNNEL_BPF)
+static int bpf_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len,
+                            bool ingress)
+{
+       return bpf_lwt_push_ip_encap(skb, hdr, len, ingress);
+}
+#endif
+
+BPF_CALL_4(bpf_lwt_in_push_encap, struct sk_buff *, skb, u32, type, void *, hdr,
           u32, len)
 {
        switch (type) {
@@ -4810,13 +4835,40 @@ BPF_CALL_4(bpf_lwt_push_encap, struct sk_buff *, skb, u32, type, void *, hdr,
        case BPF_LWT_ENCAP_SEG6_INLINE:
                return bpf_push_seg6_encap(skb, type, hdr, len);
 #endif
+#if IS_ENABLED(CONFIG_LWTUNNEL_BPF)
+       case BPF_LWT_ENCAP_IP:
+               return bpf_push_ip_encap(skb, hdr, len, true /* ingress */);
+#endif
        default:
                return -EINVAL;
        }
 }
 
-static const struct bpf_func_proto bpf_lwt_push_encap_proto = {
-       .func           = bpf_lwt_push_encap,
+BPF_CALL_4(bpf_lwt_xmit_push_encap, struct sk_buff *, skb, u32, type,
+          void *, hdr, u32, len)
+{
+       switch (type) {
+#if IS_ENABLED(CONFIG_LWTUNNEL_BPF)
+       case BPF_LWT_ENCAP_IP:
+               return bpf_push_ip_encap(skb, hdr, len, false /* egress */);
+#endif
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct bpf_func_proto bpf_lwt_in_push_encap_proto = {
+       .func           = bpf_lwt_in_push_encap,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_ANYTHING,
+       .arg3_type      = ARG_PTR_TO_MEM,
+       .arg4_type      = ARG_CONST_SIZE
+};
+
+static const struct bpf_func_proto bpf_lwt_xmit_push_encap_proto = {
+       .func           = bpf_lwt_xmit_push_encap,
        .gpl_only       = false,
        .ret_type       = RET_INTEGER,
        .arg1_type      = ARG_PTR_TO_CTX,
@@ -5016,6 +5068,54 @@ static const struct bpf_func_proto bpf_lwt_seg6_adjust_srh_proto = {
 };
 #endif /* CONFIG_IPV6_SEG6_BPF */
 
+#define CONVERT_COMMON_TCP_SOCK_FIELDS(md_type, CONVERT)               \
+do {                                                                   \
+       switch (si->off) {                                              \
+       case offsetof(md_type, snd_cwnd):                               \
+               CONVERT(snd_cwnd); break;                               \
+       case offsetof(md_type, srtt_us):                                \
+               CONVERT(srtt_us); break;                                \
+       case offsetof(md_type, snd_ssthresh):                           \
+               CONVERT(snd_ssthresh); break;                           \
+       case offsetof(md_type, rcv_nxt):                                \
+               CONVERT(rcv_nxt); break;                                \
+       case offsetof(md_type, snd_nxt):                                \
+               CONVERT(snd_nxt); break;                                \
+       case offsetof(md_type, snd_una):                                \
+               CONVERT(snd_una); break;                                \
+       case offsetof(md_type, mss_cache):                              \
+               CONVERT(mss_cache); break;                              \
+       case offsetof(md_type, ecn_flags):                              \
+               CONVERT(ecn_flags); break;                              \
+       case offsetof(md_type, rate_delivered):                         \
+               CONVERT(rate_delivered); break;                         \
+       case offsetof(md_type, rate_interval_us):                       \
+               CONVERT(rate_interval_us); break;                       \
+       case offsetof(md_type, packets_out):                            \
+               CONVERT(packets_out); break;                            \
+       case offsetof(md_type, retrans_out):                            \
+               CONVERT(retrans_out); break;                            \
+       case offsetof(md_type, total_retrans):                          \
+               CONVERT(total_retrans); break;                          \
+       case offsetof(md_type, segs_in):                                \
+               CONVERT(segs_in); break;                                \
+       case offsetof(md_type, data_segs_in):                           \
+               CONVERT(data_segs_in); break;                           \
+       case offsetof(md_type, segs_out):                               \
+               CONVERT(segs_out); break;                               \
+       case offsetof(md_type, data_segs_out):                          \
+               CONVERT(data_segs_out); break;                          \
+       case offsetof(md_type, lost_out):                               \
+               CONVERT(lost_out); break;                               \
+       case offsetof(md_type, sacked_out):                             \
+               CONVERT(sacked_out); break;                             \
+       case offsetof(md_type, bytes_received):                         \
+               CONVERT(bytes_received); break;                         \
+       case offsetof(md_type, bytes_acked):                            \
+               CONVERT(bytes_acked); break;                            \
+       }                                                               \
+} while (0)
+
 #ifdef CONFIG_INET
 static struct sock *sk_lookup(struct net *net, struct bpf_sock_tuple *tuple,
                              int dif, int sdif, u8 family, u8 proto)
@@ -5253,6 +5353,79 @@ static const struct bpf_func_proto bpf_sock_addr_sk_lookup_udp_proto = {
        .arg5_type      = ARG_ANYTHING,
 };
 
+bool bpf_tcp_sock_is_valid_access(int off, int size, enum bpf_access_type type,
+                                 struct bpf_insn_access_aux *info)
+{
+       if (off < 0 || off >= offsetofend(struct bpf_tcp_sock, bytes_acked))
+               return false;
+
+       if (off % size != 0)
+               return false;
+
+       switch (off) {
+       case offsetof(struct bpf_tcp_sock, bytes_received):
+       case offsetof(struct bpf_tcp_sock, bytes_acked):
+               return size == sizeof(__u64);
+       default:
+               return size == sizeof(__u32);
+       }
+}
+
+u32 bpf_tcp_sock_convert_ctx_access(enum bpf_access_type type,
+                                   const struct bpf_insn *si,
+                                   struct bpf_insn *insn_buf,
+                                   struct bpf_prog *prog, u32 *target_size)
+{
+       struct bpf_insn *insn = insn_buf;
+
+#define BPF_TCP_SOCK_GET_COMMON(FIELD)                                 \
+       do {                                                            \
+               BUILD_BUG_ON(FIELD_SIZEOF(struct tcp_sock, FIELD) >     \
+                            FIELD_SIZEOF(struct bpf_tcp_sock, FIELD)); \
+               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct tcp_sock, FIELD),\
+                                     si->dst_reg, si->src_reg,         \
+                                     offsetof(struct tcp_sock, FIELD)); \
+       } while (0)
+
+       CONVERT_COMMON_TCP_SOCK_FIELDS(struct bpf_tcp_sock,
+                                      BPF_TCP_SOCK_GET_COMMON);
+
+       if (insn > insn_buf)
+               return insn - insn_buf;
+
+       switch (si->off) {
+       case offsetof(struct bpf_tcp_sock, rtt_min):
+               BUILD_BUG_ON(FIELD_SIZEOF(struct tcp_sock, rtt_min) !=
+                            sizeof(struct minmax));
+               BUILD_BUG_ON(sizeof(struct minmax) <
+                            sizeof(struct minmax_sample));
+
+               *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
+                                     offsetof(struct tcp_sock, rtt_min) +
+                                     offsetof(struct minmax_sample, v));
+               break;
+       }
+
+       return insn - insn_buf;
+}
+
+BPF_CALL_1(bpf_tcp_sock, struct sock *, sk)
+{
+       sk = sk_to_full_sk(sk);
+
+       if (sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP)
+               return (unsigned long)sk;
+
+       return (unsigned long)NULL;
+}
+
+static const struct bpf_func_proto bpf_tcp_sock_proto = {
+       .func           = bpf_tcp_sock,
+       .gpl_only       = false,
+       .ret_type       = RET_PTR_TO_TCP_SOCK_OR_NULL,
+       .arg1_type      = ARG_PTR_TO_SOCK_COMMON,
+};
+
 #endif /* CONFIG_INET */
 
 bool bpf_helper_changes_pkt_data(void *func)
@@ -5282,7 +5455,8 @@ bool bpf_helper_changes_pkt_data(void *func)
            func == bpf_lwt_seg6_adjust_srh ||
            func == bpf_lwt_seg6_action ||
 #endif
-           func == bpf_lwt_push_encap)
+           func == bpf_lwt_in_push_encap ||
+           func == bpf_lwt_xmit_push_encap)
                return true;
 
        return false;
@@ -5314,10 +5488,20 @@ bpf_base_func_proto(enum bpf_func_id func_id)
                return &bpf_tail_call_proto;
        case BPF_FUNC_ktime_get_ns:
                return &bpf_ktime_get_ns_proto;
+       default:
+               break;
+       }
+
+       if (!capable(CAP_SYS_ADMIN))
+               return NULL;
+
+       switch (func_id) {
+       case BPF_FUNC_spin_lock:
+               return &bpf_spin_lock_proto;
+       case BPF_FUNC_spin_unlock:
+               return &bpf_spin_unlock_proto;
        case BPF_FUNC_trace_printk:
-               if (capable(CAP_SYS_ADMIN))
-                       return bpf_get_trace_printk_proto();
-               /* else, fall through */
+               return bpf_get_trace_printk_proto();
        default:
                return NULL;
        }
@@ -5396,6 +5580,12 @@ cg_skb_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
        switch (func_id) {
        case BPF_FUNC_get_local_storage:
                return &bpf_get_local_storage_proto;
+       case BPF_FUNC_sk_fullsock:
+               return &bpf_sk_fullsock_proto;
+#ifdef CONFIG_INET
+       case BPF_FUNC_tcp_sock:
+               return &bpf_tcp_sock_proto;
+#endif
        default:
                return sk_filter_func_proto(func_id, prog);
        }
@@ -5467,6 +5657,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_get_socket_uid_proto;
        case BPF_FUNC_fib_lookup:
                return &bpf_skb_fib_lookup_proto;
+       case BPF_FUNC_sk_fullsock:
+               return &bpf_sk_fullsock_proto;
 #ifdef CONFIG_XFRM
        case BPF_FUNC_skb_get_xfrm_state:
                return &bpf_skb_get_xfrm_state_proto;
@@ -5484,6 +5676,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_sk_lookup_udp_proto;
        case BPF_FUNC_sk_release:
                return &bpf_sk_release_proto;
+       case BPF_FUNC_tcp_sock:
+               return &bpf_tcp_sock_proto;
 #endif
        default:
                return bpf_base_func_proto(func_id);
@@ -5660,7 +5854,7 @@ lwt_in_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
        switch (func_id) {
        case BPF_FUNC_lwt_push_encap:
-               return &bpf_lwt_push_encap_proto;
+               return &bpf_lwt_in_push_encap_proto;
        default:
                return lwt_out_func_proto(func_id, prog);
        }
@@ -5696,6 +5890,8 @@ lwt_xmit_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_l4_csum_replace_proto;
        case BPF_FUNC_set_hash_invalid:
                return &bpf_set_hash_invalid_proto;
+       case BPF_FUNC_lwt_push_encap:
+               return &bpf_lwt_xmit_push_encap_proto;
        default:
                return lwt_out_func_proto(func_id, prog);
        }
@@ -5754,6 +5950,11 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type
                if (size != sizeof(__u64))
                        return false;
                break;
+       case offsetof(struct __sk_buff, sk):
+               if (type == BPF_WRITE || size != sizeof(__u64))
+                       return false;
+               info->reg_type = PTR_TO_SOCK_COMMON_OR_NULL;
+               break;
        default:
                /* Only narrow read access allowed for now. */
                if (type == BPF_WRITE) {
@@ -5925,31 +6126,44 @@ full_access:
        return true;
 }
 
-static bool __sock_filter_check_size(int off, int size,
+bool bpf_sock_common_is_valid_access(int off, int size,
+                                    enum bpf_access_type type,
                                     struct bpf_insn_access_aux *info)
 {
-       const int size_default = sizeof(__u32);
-
        switch (off) {
-       case bpf_ctx_range(struct bpf_sock, src_ip4):
-       case bpf_ctx_range_till(struct bpf_sock, src_ip6[0], src_ip6[3]):
-               bpf_ctx_record_field_size(info, size_default);
-               return bpf_ctx_narrow_access_ok(off, size, size_default);
+       case bpf_ctx_range_till(struct bpf_sock, type, priority):
+               return false;
+       default:
+               return bpf_sock_is_valid_access(off, size, type, info);
        }
-
-       return size == size_default;
 }
 
 bool bpf_sock_is_valid_access(int off, int size, enum bpf_access_type type,
                              struct bpf_insn_access_aux *info)
 {
+       const int size_default = sizeof(__u32);
+
        if (off < 0 || off >= sizeof(struct bpf_sock))
                return false;
        if (off % size != 0)
                return false;
-       if (!__sock_filter_check_size(off, size, info))
-               return false;
-       return true;
+
+       switch (off) {
+       case offsetof(struct bpf_sock, state):
+       case offsetof(struct bpf_sock, family):
+       case offsetof(struct bpf_sock, type):
+       case offsetof(struct bpf_sock, protocol):
+       case offsetof(struct bpf_sock, dst_port):
+       case offsetof(struct bpf_sock, src_port):
+       case bpf_ctx_range(struct bpf_sock, src_ip4):
+       case bpf_ctx_range_till(struct bpf_sock, src_ip6[0], src_ip6[3]):
+       case bpf_ctx_range(struct bpf_sock, dst_ip4):
+       case bpf_ctx_range_till(struct bpf_sock, dst_ip6[0], dst_ip6[3]):
+               bpf_ctx_record_field_size(info, size_default);
+               return bpf_ctx_narrow_access_ok(off, size, size_default);
+       }
+
+       return size == size_default;
 }
 
 static bool sock_filter_is_valid_access(int off, int size,
@@ -6738,6 +6952,13 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type,
                off += offsetof(struct qdisc_skb_cb, pkt_len);
                *target_size = 4;
                *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, off);
+               break;
+
+       case offsetof(struct __sk_buff, sk):
+               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk),
+                                     si->dst_reg, si->src_reg,
+                                     offsetof(struct sk_buff, sk));
+               break;
        }
 
        return insn - insn_buf;
@@ -6786,24 +7007,32 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type,
                break;
 
        case offsetof(struct bpf_sock, family):
-               BUILD_BUG_ON(FIELD_SIZEOF(struct sock, sk_family) != 2);
-
-               *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
-                                     offsetof(struct sock, sk_family));
+               *insn++ = BPF_LDX_MEM(
+                       BPF_FIELD_SIZEOF(struct sock_common, skc_family),
+                       si->dst_reg, si->src_reg,
+                       bpf_target_off(struct sock_common,
+                                      skc_family,
+                                      FIELD_SIZEOF(struct sock_common,
+                                                   skc_family),
+                                      target_size));
                break;
 
        case offsetof(struct bpf_sock, type):
+               BUILD_BUG_ON(HWEIGHT32(SK_FL_TYPE_MASK) != BITS_PER_BYTE * 2);
                *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
                                      offsetof(struct sock, __sk_flags_offset));
                *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, SK_FL_TYPE_MASK);
                *insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, SK_FL_TYPE_SHIFT);
+               *target_size = 2;
                break;
 
        case offsetof(struct bpf_sock, protocol):
+               BUILD_BUG_ON(HWEIGHT32(SK_FL_PROTO_MASK) != BITS_PER_BYTE);
                *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
                                      offsetof(struct sock, __sk_flags_offset));
                *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, SK_FL_PROTO_MASK);
                *insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, SK_FL_PROTO_SHIFT);
+               *target_size = 1;
                break;
 
        case offsetof(struct bpf_sock, src_ip4):
@@ -6815,6 +7044,15 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type,
                                       target_size));
                break;
 
+       case offsetof(struct bpf_sock, dst_ip4):
+               *insn++ = BPF_LDX_MEM(
+                       BPF_SIZE(si->code), si->dst_reg, si->src_reg,
+                       bpf_target_off(struct sock_common, skc_daddr,
+                                      FIELD_SIZEOF(struct sock_common,
+                                                   skc_daddr),
+                                      target_size));
+               break;
+
        case bpf_ctx_range_till(struct bpf_sock, src_ip6[0], src_ip6[3]):
 #if IS_ENABLED(CONFIG_IPV6)
                off = si->off;
@@ -6833,6 +7071,23 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type,
 #endif
                break;
 
+       case bpf_ctx_range_till(struct bpf_sock, dst_ip6[0], dst_ip6[3]):
+#if IS_ENABLED(CONFIG_IPV6)
+               off = si->off;
+               off -= offsetof(struct bpf_sock, dst_ip6[0]);
+               *insn++ = BPF_LDX_MEM(
+                       BPF_SIZE(si->code), si->dst_reg, si->src_reg,
+                       bpf_target_off(struct sock_common,
+                                      skc_v6_daddr.s6_addr32[0],
+                                      FIELD_SIZEOF(struct sock_common,
+                                                   skc_v6_daddr.s6_addr32[0]),
+                                      target_size) + off);
+#else
+               *insn++ = BPF_MOV32_IMM(si->dst_reg, 0);
+               *target_size = 4;
+#endif
+               break;
+
        case offsetof(struct bpf_sock, src_port):
                *insn++ = BPF_LDX_MEM(
                        BPF_FIELD_SIZEOF(struct sock_common, skc_num),
@@ -6842,6 +7097,26 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type,
                                                    skc_num),
                                       target_size));
                break;
+
+       case offsetof(struct bpf_sock, dst_port):
+               *insn++ = BPF_LDX_MEM(
+                       BPF_FIELD_SIZEOF(struct sock_common, skc_dport),
+                       si->dst_reg, si->src_reg,
+                       bpf_target_off(struct sock_common, skc_dport,
+                                      FIELD_SIZEOF(struct sock_common,
+                                                   skc_dport),
+                                      target_size));
+               break;
+
+       case offsetof(struct bpf_sock, state):
+               *insn++ = BPF_LDX_MEM(
+                       BPF_FIELD_SIZEOF(struct sock_common, skc_state),
+                       si->dst_reg, si->src_reg,
+                       bpf_target_off(struct sock_common, skc_state,
+                                      FIELD_SIZEOF(struct sock_common,
+                                                   skc_state),
+                                      target_size));
+               break;
        }
 
        return insn - insn_buf;
@@ -7089,6 +7364,85 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
        struct bpf_insn *insn = insn_buf;
        int off;
 
+/* Helper macro for adding read access to tcp_sock or sock fields. */
+#define SOCK_OPS_GET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ)                        \
+       do {                                                                  \
+               BUILD_BUG_ON(FIELD_SIZEOF(OBJ, OBJ_FIELD) >                   \
+                            FIELD_SIZEOF(struct bpf_sock_ops, BPF_FIELD));   \
+               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(                       \
+                                               struct bpf_sock_ops_kern,     \
+                                               is_fullsock),                 \
+                                     si->dst_reg, si->src_reg,               \
+                                     offsetof(struct bpf_sock_ops_kern,      \
+                                              is_fullsock));                 \
+               *insn++ = BPF_JMP_IMM(BPF_JEQ, si->dst_reg, 0, 2);            \
+               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(                       \
+                                               struct bpf_sock_ops_kern, sk),\
+                                     si->dst_reg, si->src_reg,               \
+                                     offsetof(struct bpf_sock_ops_kern, sk));\
+               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(OBJ,                   \
+                                                      OBJ_FIELD),            \
+                                     si->dst_reg, si->dst_reg,               \
+                                     offsetof(OBJ, OBJ_FIELD));              \
+       } while (0)
+
+#define SOCK_OPS_GET_TCP_SOCK_FIELD(FIELD) \
+               SOCK_OPS_GET_FIELD(FIELD, FIELD, struct tcp_sock)
+
+/* Helper macro for adding write access to tcp_sock or sock fields.
+ * The macro is called with two registers, dst_reg which contains a pointer
+ * to ctx (context) and src_reg which contains the value that should be
+ * stored. However, we need an additional register since we cannot overwrite
+ * dst_reg because it may be used later in the program.
+ * Instead we "borrow" one of the other register. We first save its value
+ * into a new (temp) field in bpf_sock_ops_kern, use it, and then restore
+ * it at the end of the macro.
+ */
+#define SOCK_OPS_SET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ)                        \
+       do {                                                                  \
+               int reg = BPF_REG_9;                                          \
+               BUILD_BUG_ON(FIELD_SIZEOF(OBJ, OBJ_FIELD) >                   \
+                            FIELD_SIZEOF(struct bpf_sock_ops, BPF_FIELD));   \
+               if (si->dst_reg == reg || si->src_reg == reg)                 \
+                       reg--;                                                \
+               if (si->dst_reg == reg || si->src_reg == reg)                 \
+                       reg--;                                                \
+               *insn++ = BPF_STX_MEM(BPF_DW, si->dst_reg, reg,               \
+                                     offsetof(struct bpf_sock_ops_kern,      \
+                                              temp));                        \
+               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(                       \
+                                               struct bpf_sock_ops_kern,     \
+                                               is_fullsock),                 \
+                                     reg, si->dst_reg,                       \
+                                     offsetof(struct bpf_sock_ops_kern,      \
+                                              is_fullsock));                 \
+               *insn++ = BPF_JMP_IMM(BPF_JEQ, reg, 0, 2);                    \
+               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(                       \
+                                               struct bpf_sock_ops_kern, sk),\
+                                     reg, si->dst_reg,                       \
+                                     offsetof(struct bpf_sock_ops_kern, sk));\
+               *insn++ = BPF_STX_MEM(BPF_FIELD_SIZEOF(OBJ, OBJ_FIELD),       \
+                                     reg, si->src_reg,                       \
+                                     offsetof(OBJ, OBJ_FIELD));              \
+               *insn++ = BPF_LDX_MEM(BPF_DW, reg, si->dst_reg,               \
+                                     offsetof(struct bpf_sock_ops_kern,      \
+                                              temp));                        \
+       } while (0)
+
+#define SOCK_OPS_GET_OR_SET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ, TYPE)           \
+       do {                                                                  \
+               if (TYPE == BPF_WRITE)                                        \
+                       SOCK_OPS_SET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ);        \
+               else                                                          \
+                       SOCK_OPS_GET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ);        \
+       } while (0)
+
+       CONVERT_COMMON_TCP_SOCK_FIELDS(struct bpf_sock_ops,
+                                      SOCK_OPS_GET_TCP_SOCK_FIELD);
+
+       if (insn > insn_buf)
+               return insn - insn_buf;
+
        switch (si->off) {
        case offsetof(struct bpf_sock_ops, op) ...
             offsetof(struct bpf_sock_ops, replylong[3]):
@@ -7246,175 +7600,15 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
                                      FIELD_SIZEOF(struct minmax_sample, t));
                break;
 
-/* Helper macro for adding read access to tcp_sock or sock fields. */
-#define SOCK_OPS_GET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ)                        \
-       do {                                                                  \
-               BUILD_BUG_ON(FIELD_SIZEOF(OBJ, OBJ_FIELD) >                   \
-                            FIELD_SIZEOF(struct bpf_sock_ops, BPF_FIELD));   \
-               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(                       \
-                                               struct bpf_sock_ops_kern,     \
-                                               is_fullsock),                 \
-                                     si->dst_reg, si->src_reg,               \
-                                     offsetof(struct bpf_sock_ops_kern,      \
-                                              is_fullsock));                 \
-               *insn++ = BPF_JMP_IMM(BPF_JEQ, si->dst_reg, 0, 2);            \
-               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(                       \
-                                               struct bpf_sock_ops_kern, sk),\
-                                     si->dst_reg, si->src_reg,               \
-                                     offsetof(struct bpf_sock_ops_kern, sk));\
-               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(OBJ,                   \
-                                                      OBJ_FIELD),            \
-                                     si->dst_reg, si->dst_reg,               \
-                                     offsetof(OBJ, OBJ_FIELD));              \
-       } while (0)
-
-/* Helper macro for adding write access to tcp_sock or sock fields.
- * The macro is called with two registers, dst_reg which contains a pointer
- * to ctx (context) and src_reg which contains the value that should be
- * stored. However, we need an additional register since we cannot overwrite
- * dst_reg because it may be used later in the program.
- * Instead we "borrow" one of the other register. We first save its value
- * into a new (temp) field in bpf_sock_ops_kern, use it, and then restore
- * it at the end of the macro.
- */
-#define SOCK_OPS_SET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ)                        \
-       do {                                                                  \
-               int reg = BPF_REG_9;                                          \
-               BUILD_BUG_ON(FIELD_SIZEOF(OBJ, OBJ_FIELD) >                   \
-                            FIELD_SIZEOF(struct bpf_sock_ops, BPF_FIELD));   \
-               if (si->dst_reg == reg || si->src_reg == reg)                 \
-                       reg--;                                                \
-               if (si->dst_reg == reg || si->src_reg == reg)                 \
-                       reg--;                                                \
-               *insn++ = BPF_STX_MEM(BPF_DW, si->dst_reg, reg,               \
-                                     offsetof(struct bpf_sock_ops_kern,      \
-                                              temp));                        \
-               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(                       \
-                                               struct bpf_sock_ops_kern,     \
-                                               is_fullsock),                 \
-                                     reg, si->dst_reg,                       \
-                                     offsetof(struct bpf_sock_ops_kern,      \
-                                              is_fullsock));                 \
-               *insn++ = BPF_JMP_IMM(BPF_JEQ, reg, 0, 2);                    \
-               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(                       \
-                                               struct bpf_sock_ops_kern, sk),\
-                                     reg, si->dst_reg,                       \
-                                     offsetof(struct bpf_sock_ops_kern, sk));\
-               *insn++ = BPF_STX_MEM(BPF_FIELD_SIZEOF(OBJ, OBJ_FIELD),       \
-                                     reg, si->src_reg,                       \
-                                     offsetof(OBJ, OBJ_FIELD));              \
-               *insn++ = BPF_LDX_MEM(BPF_DW, reg, si->dst_reg,               \
-                                     offsetof(struct bpf_sock_ops_kern,      \
-                                              temp));                        \
-       } while (0)
-
-#define SOCK_OPS_GET_OR_SET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ, TYPE)           \
-       do {                                                                  \
-               if (TYPE == BPF_WRITE)                                        \
-                       SOCK_OPS_SET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ);        \
-               else                                                          \
-                       SOCK_OPS_GET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ);        \
-       } while (0)
-
-       case offsetof(struct bpf_sock_ops, snd_cwnd):
-               SOCK_OPS_GET_FIELD(snd_cwnd, snd_cwnd, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, srtt_us):
-               SOCK_OPS_GET_FIELD(srtt_us, srtt_us, struct tcp_sock);
-               break;
-
        case offsetof(struct bpf_sock_ops, bpf_sock_ops_cb_flags):
                SOCK_OPS_GET_FIELD(bpf_sock_ops_cb_flags, bpf_sock_ops_cb_flags,
                                   struct tcp_sock);
                break;
 
-       case offsetof(struct bpf_sock_ops, snd_ssthresh):
-               SOCK_OPS_GET_FIELD(snd_ssthresh, snd_ssthresh, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, rcv_nxt):
-               SOCK_OPS_GET_FIELD(rcv_nxt, rcv_nxt, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, snd_nxt):
-               SOCK_OPS_GET_FIELD(snd_nxt, snd_nxt, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, snd_una):
-               SOCK_OPS_GET_FIELD(snd_una, snd_una, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, mss_cache):
-               SOCK_OPS_GET_FIELD(mss_cache, mss_cache, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, ecn_flags):
-               SOCK_OPS_GET_FIELD(ecn_flags, ecn_flags, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, rate_delivered):
-               SOCK_OPS_GET_FIELD(rate_delivered, rate_delivered,
-                                  struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, rate_interval_us):
-               SOCK_OPS_GET_FIELD(rate_interval_us, rate_interval_us,
-                                  struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, packets_out):
-               SOCK_OPS_GET_FIELD(packets_out, packets_out, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, retrans_out):
-               SOCK_OPS_GET_FIELD(retrans_out, retrans_out, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, total_retrans):
-               SOCK_OPS_GET_FIELD(total_retrans, total_retrans,
-                                  struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, segs_in):
-               SOCK_OPS_GET_FIELD(segs_in, segs_in, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, data_segs_in):
-               SOCK_OPS_GET_FIELD(data_segs_in, data_segs_in, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, segs_out):
-               SOCK_OPS_GET_FIELD(segs_out, segs_out, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, data_segs_out):
-               SOCK_OPS_GET_FIELD(data_segs_out, data_segs_out,
-                                  struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, lost_out):
-               SOCK_OPS_GET_FIELD(lost_out, lost_out, struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, sacked_out):
-               SOCK_OPS_GET_FIELD(sacked_out, sacked_out, struct tcp_sock);
-               break;
-
        case offsetof(struct bpf_sock_ops, sk_txhash):
                SOCK_OPS_GET_OR_SET_FIELD(sk_txhash, sk_txhash,
                                          struct sock, type);
                break;
-
-       case offsetof(struct bpf_sock_ops, bytes_received):
-               SOCK_OPS_GET_FIELD(bytes_received, bytes_received,
-                                  struct tcp_sock);
-               break;
-
-       case offsetof(struct bpf_sock_ops, bytes_acked):
-               SOCK_OPS_GET_FIELD(bytes_acked, bytes_acked, struct tcp_sock);
-               break;
-
        }
        return insn - insn_buf;
 }
diff --git a/net/core/flow_offload.c b/net/core/flow_offload.c
new file mode 100644 (file)
index 0000000..c3a00ea
--- /dev/null
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <net/flow_offload.h>
+
+struct flow_rule *flow_rule_alloc(unsigned int num_actions)
+{
+       struct flow_rule *rule;
+
+       rule = kzalloc(sizeof(struct flow_rule) +
+                      sizeof(struct flow_action_entry) * num_actions,
+                      GFP_KERNEL);
+       if (!rule)
+               return NULL;
+
+       rule->action.num_entries = num_actions;
+
+       return rule;
+}
+EXPORT_SYMBOL(flow_rule_alloc);
+
+#define FLOW_DISSECTOR_MATCH(__rule, __type, __out)                            \
+       const struct flow_match *__m = &(__rule)->match;                        \
+       struct flow_dissector *__d = (__m)->dissector;                          \
+                                                                               \
+       (__out)->key = skb_flow_dissector_target(__d, __type, (__m)->key);      \
+       (__out)->mask = skb_flow_dissector_target(__d, __type, (__m)->mask);    \
+
+void flow_rule_match_basic(const struct flow_rule *rule,
+                          struct flow_match_basic *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_BASIC, out);
+}
+EXPORT_SYMBOL(flow_rule_match_basic);
+
+void flow_rule_match_control(const struct flow_rule *rule,
+                            struct flow_match_control *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CONTROL, out);
+}
+EXPORT_SYMBOL(flow_rule_match_control);
+
+void flow_rule_match_eth_addrs(const struct flow_rule *rule,
+                              struct flow_match_eth_addrs *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS, out);
+}
+EXPORT_SYMBOL(flow_rule_match_eth_addrs);
+
+void flow_rule_match_vlan(const struct flow_rule *rule,
+                         struct flow_match_vlan *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_VLAN, out);
+}
+EXPORT_SYMBOL(flow_rule_match_vlan);
+
+void flow_rule_match_ipv4_addrs(const struct flow_rule *rule,
+                               struct flow_match_ipv4_addrs *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS, out);
+}
+EXPORT_SYMBOL(flow_rule_match_ipv4_addrs);
+
+void flow_rule_match_ipv6_addrs(const struct flow_rule *rule,
+                               struct flow_match_ipv6_addrs *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS, out);
+}
+EXPORT_SYMBOL(flow_rule_match_ipv6_addrs);
+
+void flow_rule_match_ip(const struct flow_rule *rule,
+                       struct flow_match_ip *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IP, out);
+}
+EXPORT_SYMBOL(flow_rule_match_ip);
+
+void flow_rule_match_ports(const struct flow_rule *rule,
+                          struct flow_match_ports *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_PORTS, out);
+}
+EXPORT_SYMBOL(flow_rule_match_ports);
+
+void flow_rule_match_tcp(const struct flow_rule *rule,
+                        struct flow_match_tcp *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_TCP, out);
+}
+EXPORT_SYMBOL(flow_rule_match_tcp);
+
+void flow_rule_match_icmp(const struct flow_rule *rule,
+                         struct flow_match_icmp *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ICMP, out);
+}
+EXPORT_SYMBOL(flow_rule_match_icmp);
+
+void flow_rule_match_mpls(const struct flow_rule *rule,
+                         struct flow_match_mpls *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_MPLS, out);
+}
+EXPORT_SYMBOL(flow_rule_match_mpls);
+
+void flow_rule_match_enc_control(const struct flow_rule *rule,
+                                struct flow_match_control *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL, out);
+}
+EXPORT_SYMBOL(flow_rule_match_enc_control);
+
+void flow_rule_match_enc_ipv4_addrs(const struct flow_rule *rule,
+                                   struct flow_match_ipv4_addrs *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, out);
+}
+EXPORT_SYMBOL(flow_rule_match_enc_ipv4_addrs);
+
+void flow_rule_match_enc_ipv6_addrs(const struct flow_rule *rule,
+                                   struct flow_match_ipv6_addrs *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, out);
+}
+EXPORT_SYMBOL(flow_rule_match_enc_ipv6_addrs);
+
+void flow_rule_match_enc_ip(const struct flow_rule *rule,
+                           struct flow_match_ip *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IP, out);
+}
+EXPORT_SYMBOL(flow_rule_match_enc_ip);
+
+void flow_rule_match_enc_ports(const struct flow_rule *rule,
+                              struct flow_match_ports *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_PORTS, out);
+}
+EXPORT_SYMBOL(flow_rule_match_enc_ports);
+
+void flow_rule_match_enc_keyid(const struct flow_rule *rule,
+                              struct flow_match_enc_keyid *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_KEYID, out);
+}
+EXPORT_SYMBOL(flow_rule_match_enc_keyid);
+
+void flow_rule_match_enc_opts(const struct flow_rule *rule,
+                             struct flow_match_enc_opts *out)
+{
+       FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_OPTS, out);
+}
+EXPORT_SYMBOL(flow_rule_match_enc_opts);
index a648568..a5c8c79 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/types.h>
 #include <linux/bpf.h>
 #include <net/lwtunnel.h>
+#include <net/gre.h>
+#include <net/ip6_route.h>
 
 struct bpf_lwt_prog {
        struct bpf_prog *prog;
@@ -55,6 +57,7 @@ static int run_lwt_bpf(struct sk_buff *skb, struct bpf_lwt_prog *lwt,
 
        switch (ret) {
        case BPF_OK:
+       case BPF_LWT_REROUTE:
                break;
 
        case BPF_REDIRECT:
@@ -87,6 +90,30 @@ static int run_lwt_bpf(struct sk_buff *skb, struct bpf_lwt_prog *lwt,
        return ret;
 }
 
+static int bpf_lwt_input_reroute(struct sk_buff *skb)
+{
+       int err = -EINVAL;
+
+       if (skb->protocol == htons(ETH_P_IP)) {
+               struct iphdr *iph = ip_hdr(skb);
+
+               err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
+                                          iph->tos, skb_dst(skb)->dev);
+       } else if (skb->protocol == htons(ETH_P_IPV6)) {
+               err = ipv6_stub->ipv6_route_input(skb);
+       } else {
+               err = -EAFNOSUPPORT;
+       }
+
+       if (err)
+               goto err;
+       return dst_input(skb);
+
+err:
+       kfree_skb(skb);
+       return err;
+}
+
 static int bpf_input(struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
@@ -98,11 +125,11 @@ static int bpf_input(struct sk_buff *skb)
                ret = run_lwt_bpf(skb, &bpf->in, dst, NO_REDIRECT);
                if (ret < 0)
                        return ret;
+               if (ret == BPF_LWT_REROUTE)
+                       return bpf_lwt_input_reroute(skb);
        }
 
        if (unlikely(!dst->lwtstate->orig_input)) {
-               pr_warn_once("orig_input not set on dst for prog %s\n",
-                            bpf->out.name);
                kfree_skb(skb);
                return -EINVAL;
        }
@@ -147,6 +174,102 @@ static int xmit_check_hhlen(struct sk_buff *skb)
        return 0;
 }
 
+static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
+{
+       struct net_device *l3mdev = l3mdev_master_dev_rcu(skb_dst(skb)->dev);
+       int oif = l3mdev ? l3mdev->ifindex : 0;
+       struct dst_entry *dst = NULL;
+       int err = -EAFNOSUPPORT;
+       struct sock *sk;
+       struct net *net;
+       bool ipv4;
+
+       if (skb->protocol == htons(ETH_P_IP))
+               ipv4 = true;
+       else if (skb->protocol == htons(ETH_P_IPV6))
+               ipv4 = false;
+       else
+               goto err;
+
+       sk = sk_to_full_sk(skb->sk);
+       if (sk) {
+               if (sk->sk_bound_dev_if)
+                       oif = sk->sk_bound_dev_if;
+               net = sock_net(sk);
+       } else {
+               net = dev_net(skb_dst(skb)->dev);
+       }
+
+       if (ipv4) {
+               struct iphdr *iph = ip_hdr(skb);
+               struct flowi4 fl4 = {};
+               struct rtable *rt;
+
+               fl4.flowi4_oif = oif;
+               fl4.flowi4_mark = skb->mark;
+               fl4.flowi4_uid = sock_net_uid(net, sk);
+               fl4.flowi4_tos = RT_TOS(iph->tos);
+               fl4.flowi4_flags = FLOWI_FLAG_ANYSRC;
+               fl4.flowi4_proto = iph->protocol;
+               fl4.daddr = iph->daddr;
+               fl4.saddr = iph->saddr;
+
+               rt = ip_route_output_key(net, &fl4);
+               if (IS_ERR(rt)) {
+                       err = PTR_ERR(rt);
+                       goto err;
+               }
+               dst = &rt->dst;
+       } else {
+               struct ipv6hdr *iph6 = ipv6_hdr(skb);
+               struct flowi6 fl6 = {};
+
+               fl6.flowi6_oif = oif;
+               fl6.flowi6_mark = skb->mark;
+               fl6.flowi6_uid = sock_net_uid(net, sk);
+               fl6.flowlabel = ip6_flowinfo(iph6);
+               fl6.flowi6_proto = iph6->nexthdr;
+               fl6.daddr = iph6->daddr;
+               fl6.saddr = iph6->saddr;
+
+               err = ipv6_stub->ipv6_dst_lookup(net, skb->sk, &dst, &fl6);
+               if (unlikely(err))
+                       goto err;
+               if (IS_ERR(dst)) {
+                       err = PTR_ERR(dst);
+                       goto err;
+               }
+       }
+       if (unlikely(dst->error)) {
+               err = dst->error;
+               dst_release(dst);
+               goto err;
+       }
+
+       /* Although skb header was reserved in bpf_lwt_push_ip_encap(), it
+        * was done for the previous dst, so we are doing it here again, in
+        * case the new dst needs much more space. The call below is a noop
+        * if there is enough header space in skb.
+        */
+       err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev));
+       if (unlikely(err))
+               goto err;
+
+       skb_dst_drop(skb);
+       skb_dst_set(skb, dst);
+
+       err = dst_output(dev_net(skb_dst(skb)->dev), skb->sk, skb);
+       if (unlikely(err))
+               goto err;
+
+       /* ip[6]_finish_output2 understand LWTUNNEL_XMIT_DONE */
+       return LWTUNNEL_XMIT_DONE;
+
+err:
+       kfree_skb(skb);
+       return err;
+}
+
 static int bpf_xmit(struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
@@ -154,11 +277,20 @@ static int bpf_xmit(struct sk_buff *skb)
 
        bpf = bpf_lwt_lwtunnel(dst->lwtstate);
        if (bpf->xmit.prog) {
+               __be16 proto = skb->protocol;
                int ret;
 
                ret = run_lwt_bpf(skb, &bpf->xmit, dst, CAN_REDIRECT);
                switch (ret) {
                case BPF_OK:
+                       /* If the header changed, e.g. via bpf_lwt_push_encap,
+                        * BPF_LWT_REROUTE below should have been used if the
+                        * protocol was also changed.
+                        */
+                       if (skb->protocol != proto) {
+                               kfree_skb(skb);
+                               return -EINVAL;
+                       }
                        /* If the header was expanded, headroom might be too
                         * small for L2 header to come, expand as needed.
                         */
@@ -169,6 +301,8 @@ static int bpf_xmit(struct sk_buff *skb)
                        return LWTUNNEL_XMIT_CONTINUE;
                case BPF_REDIRECT:
                        return LWTUNNEL_XMIT_DONE;
+               case BPF_LWT_REROUTE:
+                       return bpf_lwt_xmit_reroute(skb);
                default:
                        return ret;
                }
@@ -390,6 +524,133 @@ static const struct lwtunnel_encap_ops bpf_encap_ops = {
        .owner          = THIS_MODULE,
 };
 
+static int handle_gso_type(struct sk_buff *skb, unsigned int gso_type,
+                          int encap_len)
+{
+       struct skb_shared_info *shinfo = skb_shinfo(skb);
+
+       gso_type |= SKB_GSO_DODGY;
+       shinfo->gso_type |= gso_type;
+       skb_decrease_gso_size(shinfo, encap_len);
+       shinfo->gso_segs = 0;
+       return 0;
+}
+
+static int handle_gso_encap(struct sk_buff *skb, bool ipv4, int encap_len)
+{
+       int next_hdr_offset;
+       void *next_hdr;
+       __u8 protocol;
+
+       /* SCTP and UDP_L4 gso need more nuanced handling than what
+        * handle_gso_type() does above: skb_decrease_gso_size() is not enough.
+        * So at the moment only TCP GSO packets are let through.
+        */
+       if (!(skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))
+               return -ENOTSUPP;
+
+       if (ipv4) {
+               protocol = ip_hdr(skb)->protocol;
+               next_hdr_offset = sizeof(struct iphdr);
+               next_hdr = skb_network_header(skb) + next_hdr_offset;
+       } else {
+               protocol = ipv6_hdr(skb)->nexthdr;
+               next_hdr_offset = sizeof(struct ipv6hdr);
+               next_hdr = skb_network_header(skb) + next_hdr_offset;
+       }
+
+       switch (protocol) {
+       case IPPROTO_GRE:
+               next_hdr_offset += sizeof(struct gre_base_hdr);
+               if (next_hdr_offset > encap_len)
+                       return -EINVAL;
+
+               if (((struct gre_base_hdr *)next_hdr)->flags & GRE_CSUM)
+                       return handle_gso_type(skb, SKB_GSO_GRE_CSUM,
+                                              encap_len);
+               return handle_gso_type(skb, SKB_GSO_GRE, encap_len);
+
+       case IPPROTO_UDP:
+               next_hdr_offset += sizeof(struct udphdr);
+               if (next_hdr_offset > encap_len)
+                       return -EINVAL;
+
+               if (((struct udphdr *)next_hdr)->check)
+                       return handle_gso_type(skb, SKB_GSO_UDP_TUNNEL_CSUM,
+                                              encap_len);
+               return handle_gso_type(skb, SKB_GSO_UDP_TUNNEL, encap_len);
+
+       case IPPROTO_IP:
+       case IPPROTO_IPV6:
+               if (ipv4)
+                       return handle_gso_type(skb, SKB_GSO_IPXIP4, encap_len);
+               else
+                       return handle_gso_type(skb, SKB_GSO_IPXIP6, encap_len);
+
+       default:
+               return -EPROTONOSUPPORT;
+       }
+}
+
+int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool ingress)
+{
+       struct iphdr *iph;
+       bool ipv4;
+       int err;
+
+       if (unlikely(len < sizeof(struct iphdr) || len > LWT_BPF_MAX_HEADROOM))
+               return -EINVAL;
+
+       /* validate protocol and length */
+       iph = (struct iphdr *)hdr;
+       if (iph->version == 4) {
+               ipv4 = true;
+               if (unlikely(len < iph->ihl * 4))
+                       return -EINVAL;
+       } else if (iph->version == 6) {
+               ipv4 = false;
+               if (unlikely(len < sizeof(struct ipv6hdr)))
+                       return -EINVAL;
+       } else {
+               return -EINVAL;
+       }
+
+       if (ingress)
+               err = skb_cow_head(skb, len + skb->mac_len);
+       else
+               err = skb_cow_head(skb,
+                                  len + LL_RESERVED_SPACE(skb_dst(skb)->dev));
+       if (unlikely(err))
+               return err;
+
+       /* push the encap headers and fix pointers */
+       skb_reset_inner_headers(skb);
+       skb->encapsulation = 1;
+       skb_push(skb, len);
+       if (ingress)
+               skb_postpush_rcsum(skb, iph, len);
+       skb_reset_network_header(skb);
+       memcpy(skb_network_header(skb), hdr, len);
+       bpf_compute_data_pointers(skb);
+       skb_clear_hash(skb);
+
+       if (ipv4) {
+               skb->protocol = htons(ETH_P_IP);
+               iph = ip_hdr(skb);
+
+               if (!iph->check)
+                       iph->check = ip_fast_csum((unsigned char *)iph,
+                                                 iph->ihl);
+       } else {
+               skb->protocol = htons(ETH_P_IPV6);
+       }
+
+       if (skb_is_gso(skb))
+               return handle_gso_encap(skb, ipv4, len);
+
+       return 0;
+}
+
 static int __init bpf_lwt_init(void)
 {
        return lwtunnel_encap_add_ops(&bpf_encap_ops, LWTUNNEL_ENCAP_BPF);
index 4230400..30f6fd8 100644 (file)
@@ -42,6 +42,8 @@
 #include <linux/inetdevice.h>
 #include <net/addrconf.h>
 
+#include <trace/events/neigh.h>
+
 #define DEBUG
 #define NEIGH_DEBUG 1
 #define neigh_dbg(level, fmt, ...)             \
@@ -102,6 +104,7 @@ static void neigh_cleanup_and_release(struct neighbour *neigh)
        if (neigh->parms->neigh_cleanup)
                neigh->parms->neigh_cleanup(neigh);
 
+       trace_neigh_cleanup_and_release(neigh, 0);
        __neigh_notify(neigh, RTM_DELNEIGH, 0, 0);
        call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh);
        neigh_release(neigh);
@@ -1095,6 +1098,8 @@ out:
        if (notify)
                neigh_update_notify(neigh, 0);
 
+       trace_neigh_timer_handler(neigh, 0);
+
        neigh_release(neigh);
 }
 
@@ -1165,6 +1170,7 @@ out_unlock_bh:
        else
                write_unlock(&neigh->lock);
        local_bh_enable();
+       trace_neigh_event_send_done(neigh, rc);
        return rc;
 
 out_dead:
@@ -1172,6 +1178,7 @@ out_dead:
                goto out_unlock_bh;
        write_unlock_bh(&neigh->lock);
        kfree_skb(skb);
+       trace_neigh_event_send_dead(neigh, 1);
        return 1;
 }
 EXPORT_SYMBOL(__neigh_event_send);
@@ -1227,6 +1234,8 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
        struct net_device *dev;
        int update_isrouter = 0;
 
+       trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid);
+
        write_lock_bh(&neigh->lock);
 
        dev    = neigh->dev;
@@ -1393,6 +1402,8 @@ out:
        if (notify)
                neigh_update_notify(neigh, nlmsg_pid);
 
+       trace_neigh_update_done(neigh, err);
+
        return err;
 }
 
index ff9fd2b..7c50611 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/capability.h>
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
-#include <net/switchdev.h>
 #include <linux/if_arp.h>
 #include <linux/slab.h>
 #include <linux/sched/signal.h>
@@ -501,16 +500,11 @@ static ssize_t phys_switch_id_show(struct device *dev,
                return restart_syscall();
 
        if (dev_isalive(netdev)) {
-               struct switchdev_attr attr = {
-                       .orig_dev = netdev,
-                       .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
-                       .flags = SWITCHDEV_F_NO_RECURSE,
-               };
+               struct netdev_phys_item_id ppid = { };
 
-               ret = switchdev_port_attr_get(netdev, &attr);
+               ret = dev_get_port_parent_id(netdev, &ppid, false);
                if (!ret)
-                       ret = sprintf(buf, "%*phN\n", attr.u.ppid.id_len,
-                                     attr.u.ppid.id);
+                       ret = sprintf(buf, "%*phN\n", ppid.id_len, ppid.id);
        }
        rtnl_unlock();
 
index 419af6d..470b179 100644 (file)
@@ -43,6 +43,14 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(fdb_delete);
 EXPORT_TRACEPOINT_SYMBOL_GPL(br_fdb_update);
 #endif
 
+#include <trace/events/neigh.h>
+EXPORT_TRACEPOINT_SYMBOL_GPL(neigh_update);
+EXPORT_TRACEPOINT_SYMBOL_GPL(neigh_update_done);
+EXPORT_TRACEPOINT_SYMBOL_GPL(neigh_timer_handler);
+EXPORT_TRACEPOINT_SYMBOL_GPL(neigh_event_send_done);
+EXPORT_TRACEPOINT_SYMBOL_GPL(neigh_event_send_dead);
+EXPORT_TRACEPOINT_SYMBOL_GPL(neigh_cleanup_and_release);
+
 EXPORT_TRACEPOINT_SYMBOL_GPL(kfree_skb);
 
 EXPORT_TRACEPOINT_SYMBOL_GPL(napi_poll);
index 43a932c..5b2252c 100644 (file)
@@ -136,17 +136,19 @@ static struct page *__page_pool_alloc_pages_slow(struct page_pool *pool,
        if (!(pool->p.flags & PP_FLAG_DMA_MAP))
                goto skip_dma_map;
 
-       /* Setup DMA mapping: use page->private for DMA-addr
+       /* Setup DMA mapping: use 'struct page' area for storing DMA-addr
+        * since dma_addr_t can be either 32 or 64 bits and does not always fit
+        * into page private data (i.e 32bit cpu with 64bit DMA caps)
         * This mapping is kept for lifetime of page, until leaving pool.
         */
-       dma = dma_map_page(pool->p.dev, page, 0,
-                          (PAGE_SIZE << pool->p.order),
-                          pool->p.dma_dir);
+       dma = dma_map_page_attrs(pool->p.dev, page, 0,
+                                (PAGE_SIZE << pool->p.order),
+                                pool->p.dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
        if (dma_mapping_error(pool->p.dev, dma)) {
                put_page(page);
                return NULL;
        }
-       set_page_private(page, dma); /* page->private = dma; */
+       page->dma_addr = dma;
 
 skip_dma_map:
        /* When page just alloc'ed is should/must have refcnt 1. */
@@ -175,13 +177,17 @@ EXPORT_SYMBOL(page_pool_alloc_pages);
 static void __page_pool_clean_page(struct page_pool *pool,
                                   struct page *page)
 {
+       dma_addr_t dma;
+
        if (!(pool->p.flags & PP_FLAG_DMA_MAP))
                return;
 
+       dma = page->dma_addr;
        /* DMA unmap */
-       dma_unmap_page(pool->p.dev, page_private(page),
-                      PAGE_SIZE << pool->p.order, pool->p.dma_dir);
-       set_page_private(page, 0);
+       dma_unmap_page_attrs(pool->p.dev, dma,
+                            PAGE_SIZE << pool->p.order, pool->p.dma_dir,
+                            DMA_ATTR_SKIP_CPU_SYNC);
+       page->dma_addr = 0;
 }
 
 /* Return a page to the page allocator, cleaning up our state */
index f5a9808..a51cab9 100644 (file)
@@ -46,7 +46,6 @@
 
 #include <linux/inet.h>
 #include <linux/netdevice.h>
-#include <net/switchdev.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <net/arp.h>
@@ -1146,22 +1145,17 @@ static int rtnl_phys_port_name_fill(struct sk_buff *skb, struct net_device *dev)
 
 static int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev)
 {
+       struct netdev_phys_item_id ppid = { };
        int err;
-       struct switchdev_attr attr = {
-               .orig_dev = dev,
-               .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
-               .flags = SWITCHDEV_F_NO_RECURSE,
-       };
 
-       err = switchdev_port_attr_get(dev, &attr);
+       err = dev_get_port_parent_id(dev, &ppid, false);
        if (err) {
                if (err == -EOPNOTSUPP)
                        return 0;
                return err;
        }
 
-       if (nla_put(skb, IFLA_PHYS_SWITCH_ID, attr.u.ppid.id_len,
-                   attr.u.ppid.id))
+       if (nla_put(skb, IFLA_PHYS_SWITCH_ID, ppid.id_len, ppid.id))
                return -EMSGSIZE;
 
        return 0;
index b1ff8a4..52ef219 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/pid.h>
 #include <linux/nsproxy.h>
 #include <linux/slab.h>
+#include <linux/errqueue.h>
 
 #include <linux/uaccess.h>
 
@@ -252,6 +253,32 @@ out:
 }
 EXPORT_SYMBOL(put_cmsg);
 
+void put_cmsg_scm_timestamping64(struct msghdr *msg, struct scm_timestamping_internal *tss_internal)
+{
+       struct scm_timestamping64 tss;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(tss.ts); i++) {
+               tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec;
+               tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec;
+       }
+
+       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_NEW, sizeof(tss), &tss);
+}
+EXPORT_SYMBOL(put_cmsg_scm_timestamping64);
+
+void put_cmsg_scm_timestamping(struct msghdr *msg, struct scm_timestamping_internal *tss_internal)
+{
+       struct scm_timestamping tss;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(tss.ts); i++)
+               tss.ts[i] = timespec64_to_timespec(tss_internal->ts[i]);
+
+       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_OLD, sizeof(tss), &tss);
+}
+EXPORT_SYMBOL(put_cmsg_scm_timestamping);
+
 void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
 {
        struct cmsghdr __user *cm
index e76ed8d..ae6f06e 100644 (file)
@@ -554,8 +554,7 @@ static void sk_psock_destroy_deferred(struct work_struct *gc)
        struct sk_psock *psock = container_of(gc, struct sk_psock, gc);
 
        /* No sk_callback_lock since already detached. */
-       if (psock->parser.enabled)
-               strp_done(&psock->parser.strp);
+       strp_done(&psock->parser.strp);
 
        cancel_work_sync(&psock->work);
 
index 900e8a9..f4b8b78 100644 (file)
@@ -335,14 +335,68 @@ int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(__sk_backlog_rcv);
 
-static int sock_set_timeout(long *timeo_p, char __user *optval, int optlen)
+static int sock_get_timeout(long timeo, void *optval, bool old_timeval)
 {
-       struct timeval tv;
+       struct __kernel_sock_timeval tv;
+       int size;
 
-       if (optlen < sizeof(tv))
-               return -EINVAL;
-       if (copy_from_user(&tv, optval, sizeof(tv)))
-               return -EFAULT;
+       if (timeo == MAX_SCHEDULE_TIMEOUT) {
+               tv.tv_sec = 0;
+               tv.tv_usec = 0;
+       } else {
+               tv.tv_sec = timeo / HZ;
+               tv.tv_usec = ((timeo % HZ) * USEC_PER_SEC) / HZ;
+       }
+
+       if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) {
+               struct old_timeval32 tv32 = { tv.tv_sec, tv.tv_usec };
+               *(struct old_timeval32 *)optval = tv32;
+               return sizeof(tv32);
+       }
+
+       if (old_timeval) {
+               struct __kernel_old_timeval old_tv;
+               old_tv.tv_sec = tv.tv_sec;
+               old_tv.tv_usec = tv.tv_usec;
+               *(struct __kernel_old_timeval *)optval = old_tv;
+               size = sizeof(old_tv);
+       } else {
+               *(struct __kernel_sock_timeval *)optval = tv;
+               size = sizeof(tv);
+       }
+
+       return size;
+}
+
+static int sock_set_timeout(long *timeo_p, char __user *optval, int optlen, bool old_timeval)
+{
+       struct __kernel_sock_timeval tv;
+
+       if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) {
+               struct old_timeval32 tv32;
+
+               if (optlen < sizeof(tv32))
+                       return -EINVAL;
+
+               if (copy_from_user(&tv32, optval, sizeof(tv32)))
+                       return -EFAULT;
+               tv.tv_sec = tv32.tv_sec;
+               tv.tv_usec = tv32.tv_usec;
+       } else if (old_timeval) {
+               struct __kernel_old_timeval old_tv;
+
+               if (optlen < sizeof(old_tv))
+                       return -EINVAL;
+               if (copy_from_user(&old_tv, optval, sizeof(old_tv)))
+                       return -EFAULT;
+               tv.tv_sec = old_tv.tv_sec;
+               tv.tv_usec = old_tv.tv_usec;
+       } else {
+               if (optlen < sizeof(tv))
+                       return -EINVAL;
+               if (copy_from_user(&tv, optval, sizeof(tv)))
+                       return -EFAULT;
+       }
        if (tv.tv_usec < 0 || tv.tv_usec >= USEC_PER_SEC)
                return -EDOM;
 
@@ -360,8 +414,8 @@ static int sock_set_timeout(long *timeo_p, char __user *optval, int optlen)
        *timeo_p = MAX_SCHEDULE_TIMEOUT;
        if (tv.tv_sec == 0 && tv.tv_usec == 0)
                return 0;
-       if (tv.tv_sec < (MAX_SCHEDULE_TIMEOUT/HZ - 1))
-               *timeo_p = tv.tv_sec * HZ + DIV_ROUND_UP(tv.tv_usec, USEC_PER_SEC / HZ);
+       if (tv.tv_sec < (MAX_SCHEDULE_TIMEOUT / HZ - 1))
+               *timeo_p = tv.tv_sec * HZ + DIV_ROUND_UP((unsigned long)tv.tv_usec, USEC_PER_SEC / HZ);
        return 0;
 }
 
@@ -731,6 +785,10 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
                 */
                val = min_t(u32, val, sysctl_wmem_max);
 set_sndbuf:
+               /* Ensure val * 2 fits into an int, to prevent max_t()
+                * from treating it as a negative value.
+                */
+               val = min_t(int, val, INT_MAX / 2);
                sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
                sk->sk_sndbuf = max_t(int, val * 2, SOCK_MIN_SNDBUF);
                /* Wake up sending tasks if we upped the value. */
@@ -742,6 +800,12 @@ set_sndbuf:
                        ret = -EPERM;
                        break;
                }
+
+               /* No negative values (to prevent underflow, as val will be
+                * multiplied by 2).
+                */
+               if (val < 0)
+                       val = 0;
                goto set_sndbuf;
 
        case SO_RCVBUF:
@@ -752,6 +816,10 @@ set_sndbuf:
                 */
                val = min_t(u32, val, sysctl_rmem_max);
 set_rcvbuf:
+               /* Ensure val * 2 fits into an int, to prevent max_t()
+                * from treating it as a negative value.
+                */
+               val = min_t(int, val, INT_MAX / 2);
                sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
                /*
                 * We double it on the way in to account for
@@ -776,6 +844,12 @@ set_rcvbuf:
                        ret = -EPERM;
                        break;
                }
+
+               /* No negative values (to prevent underflow, as val will be
+                * multiplied by 2).
+                */
+               if (val < 0)
+                       val = 0;
                goto set_rcvbuf;
 
        case SO_KEEPALIVE:
@@ -833,10 +907,17 @@ set_rcvbuf:
                        clear_bit(SOCK_PASSCRED, &sock->flags);
                break;
 
-       case SO_TIMESTAMP:
-       case SO_TIMESTAMPNS:
+       case SO_TIMESTAMP_OLD:
+       case SO_TIMESTAMP_NEW:
+       case SO_TIMESTAMPNS_OLD:
+       case SO_TIMESTAMPNS_NEW:
                if (valbool)  {
-                       if (optname == SO_TIMESTAMP)
+                       if (optname == SO_TIMESTAMP_NEW || optname == SO_TIMESTAMPNS_NEW)
+                               sock_set_flag(sk, SOCK_TSTAMP_NEW);
+                       else
+                               sock_reset_flag(sk, SOCK_TSTAMP_NEW);
+
+                       if (optname == SO_TIMESTAMP_OLD || optname == SO_TIMESTAMP_NEW)
                                sock_reset_flag(sk, SOCK_RCVTSTAMPNS);
                        else
                                sock_set_flag(sk, SOCK_RCVTSTAMPNS);
@@ -845,10 +926,14 @@ set_rcvbuf:
                } else {
                        sock_reset_flag(sk, SOCK_RCVTSTAMP);
                        sock_reset_flag(sk, SOCK_RCVTSTAMPNS);
+                       sock_reset_flag(sk, SOCK_TSTAMP_NEW);
                }
                break;
 
-       case SO_TIMESTAMPING:
+       case SO_TIMESTAMPING_NEW:
+               sock_set_flag(sk, SOCK_TSTAMP_NEW);
+               /* fall through */
+       case SO_TIMESTAMPING_OLD:
                if (val & ~SOF_TIMESTAMPING_MASK) {
                        ret = -EINVAL;
                        break;
@@ -879,9 +964,13 @@ set_rcvbuf:
                if (val & SOF_TIMESTAMPING_RX_SOFTWARE)
                        sock_enable_timestamp(sk,
                                              SOCK_TIMESTAMPING_RX_SOFTWARE);
-               else
+               else {
+                       if (optname == SO_TIMESTAMPING_NEW)
+                               sock_reset_flag(sk, SOCK_TSTAMP_NEW);
+
                        sock_disable_timestamp(sk,
                                               (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE));
+               }
                break;
 
        case SO_RCVLOWAT:
@@ -893,12 +982,14 @@ set_rcvbuf:
                        sk->sk_rcvlowat = val ? : 1;
                break;
 
-       case SO_RCVTIMEO:
-               ret = sock_set_timeout(&sk->sk_rcvtimeo, optval, optlen);
+       case SO_RCVTIMEO_OLD:
+       case SO_RCVTIMEO_NEW:
+               ret = sock_set_timeout(&sk->sk_rcvtimeo, optval, optlen, optname == SO_RCVTIMEO_OLD);
                break;
 
-       case SO_SNDTIMEO:
-               ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen);
+       case SO_SNDTIMEO_OLD:
+       case SO_SNDTIMEO_NEW:
+               ret = sock_set_timeout(&sk->sk_sndtimeo, optval, optlen, optname == SO_SNDTIMEO_OLD);
                break;
 
        case SO_ATTACH_FILTER:
@@ -1121,7 +1212,9 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                int val;
                u64 val64;
                struct linger ling;
-               struct timeval tm;
+               struct old_timeval32 tm32;
+               struct __kernel_old_timeval tm;
+               struct  __kernel_sock_timeval stm;
                struct sock_txtime txtime;
        } v;
 
@@ -1208,39 +1301,36 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                sock_warn_obsolete_bsdism("getsockopt");
                break;
 
-       case SO_TIMESTAMP:
+       case SO_TIMESTAMP_OLD:
                v.val = sock_flag(sk, SOCK_RCVTSTAMP) &&
+                               !sock_flag(sk, SOCK_TSTAMP_NEW) &&
                                !sock_flag(sk, SOCK_RCVTSTAMPNS);
                break;
 
-       case SO_TIMESTAMPNS:
-               v.val = sock_flag(sk, SOCK_RCVTSTAMPNS);
+       case SO_TIMESTAMPNS_OLD:
+               v.val = sock_flag(sk, SOCK_RCVTSTAMPNS) && !sock_flag(sk, SOCK_TSTAMP_NEW);
+               break;
+
+       case SO_TIMESTAMP_NEW:
+               v.val = sock_flag(sk, SOCK_RCVTSTAMP) && sock_flag(sk, SOCK_TSTAMP_NEW);
                break;
 
-       case SO_TIMESTAMPING:
+       case SO_TIMESTAMPNS_NEW:
+               v.val = sock_flag(sk, SOCK_RCVTSTAMPNS) && sock_flag(sk, SOCK_TSTAMP_NEW);
+               break;
+
+       case SO_TIMESTAMPING_OLD:
                v.val = sk->sk_tsflags;
                break;
 
-       case SO_RCVTIMEO:
-               lv = sizeof(struct timeval);
-               if (sk->sk_rcvtimeo == MAX_SCHEDULE_TIMEOUT) {
-                       v.tm.tv_sec = 0;
-                       v.tm.tv_usec = 0;
-               } else {
-                       v.tm.tv_sec = sk->sk_rcvtimeo / HZ;
-                       v.tm.tv_usec = ((sk->sk_rcvtimeo % HZ) * USEC_PER_SEC) / HZ;
-               }
+       case SO_RCVTIMEO_OLD:
+       case SO_RCVTIMEO_NEW:
+               lv = sock_get_timeout(sk->sk_rcvtimeo, &v, SO_RCVTIMEO_OLD == optname);
                break;
 
-       case SO_SNDTIMEO:
-               lv = sizeof(struct timeval);
-               if (sk->sk_sndtimeo == MAX_SCHEDULE_TIMEOUT) {
-                       v.tm.tv_sec = 0;
-                       v.tm.tv_usec = 0;
-               } else {
-                       v.tm.tv_sec = sk->sk_sndtimeo / HZ;
-                       v.tm.tv_usec = ((sk->sk_sndtimeo % HZ) * USEC_PER_SEC) / HZ;
-               }
+       case SO_SNDTIMEO_OLD:
+       case SO_SNDTIMEO_NEW:
+               lv = sock_get_timeout(sk->sk_sndtimeo, &v, SO_SNDTIMEO_OLD == optname);
                break;
 
        case SO_RCVLOWAT:
@@ -2147,7 +2237,7 @@ int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg,
                        return -EINVAL;
                sockc->mark = *(u32 *)CMSG_DATA(cmsg);
                break;
-       case SO_TIMESTAMPING:
+       case SO_TIMESTAMPING_OLD:
                if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32)))
                        return -EINVAL;
 
@@ -2405,7 +2495,7 @@ int __sk_mem_raise_allocated(struct sock *sk, int size, int amt, int kind)
        }
 
        if (sk_has_memory_pressure(sk)) {
-               int alloc;
+               u64 alloc;
 
                if (!sk_under_memory_pressure(sk))
                        return 1;
index 6eb837a..baaaeb2 100644 (file)
@@ -202,7 +202,7 @@ static inline void ccid_hc_tx_packet_recv(struct ccid *ccid, struct sock *sk,
 static inline int ccid_hc_tx_parse_options(struct ccid *ccid, struct sock *sk,
                                           u8 pkt, u8 opt, u8 *val, u8 len)
 {
-       if (ccid->ccid_ops->ccid_hc_tx_parse_options == NULL)
+       if (!ccid || !ccid->ccid_ops->ccid_hc_tx_parse_options)
                return 0;
        return ccid->ccid_ops->ccid_hc_tx_parse_options(sk, pkt, opt, val, len);
 }
@@ -214,7 +214,7 @@ static inline int ccid_hc_tx_parse_options(struct ccid *ccid, struct sock *sk,
 static inline int ccid_hc_rx_parse_options(struct ccid *ccid, struct sock *sk,
                                           u8 pkt, u8 opt, u8 *val, u8 len)
 {
-       if (ccid->ccid_ops->ccid_hc_rx_parse_options == NULL)
+       if (!ccid || !ccid->ccid_ops->ccid_hc_rx_parse_options)
                return 0;
        return ccid->ccid_ops->ccid_hc_rx_parse_options(sk, pkt, opt, val, len);
 }
index d0b3e69..0962f92 100644 (file)
@@ -56,7 +56,7 @@
 #include <net/dn_neigh.h>
 #include <net/dn_fib.h>
 
-#define DN_IFREQ_SIZE (sizeof(struct ifreq) - sizeof(struct sockaddr) + sizeof(struct sockaddr_dn))
+#define DN_IFREQ_SIZE (offsetof(struct ifreq, ifr_ifru) + sizeof(struct sockaddr_dn))
 
 static char dn_rt_all_end_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x04,0x00,0x00};
 static char dn_rt_all_rt_mcast[ETH_ALEN]  = {0xAB,0x00,0x00,0x03,0x00,0x00};
index a191702..8c431e0 100644 (file)
@@ -767,11 +767,10 @@ static int dsa_switch_probe(struct dsa_switch *ds)
 
 struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n)
 {
-       size_t size = sizeof(struct dsa_switch) + n * sizeof(struct dsa_port);
        struct dsa_switch *ds;
        int i;
 
-       ds = devm_kzalloc(dev, size, GFP_KERNEL);
+       ds = devm_kzalloc(dev, struct_size(ds, ports, n), GFP_KERNEL);
        if (!ds)
                return NULL;
 
index 79e97d2..c58f339 100644 (file)
@@ -248,6 +248,8 @@ static void dsa_master_reset_mtu(struct net_device *dev)
        rtnl_unlock();
 }
 
+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 ret;
@@ -261,6 +263,8 @@ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
        wmb();
 
        dev->dsa_ptr = cpu_dp;
+       lockdep_set_class(&dev->addr_list_lock,
+                         &dsa_master_addr_list_lock_key);
 
        ret = dsa_master_ethtool_setup(dev);
        if (ret)
index 91de3a6..2e5e7c0 100644 (file)
@@ -140,11 +140,14 @@ static int dsa_slave_close(struct net_device *dev)
 static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
 {
        struct net_device *master = dsa_slave_to_master(dev);
-
-       if (change & IFF_ALLMULTI)
-               dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1);
-       if (change & IFF_PROMISC)
-               dev_set_promiscuity(master, dev->flags & IFF_PROMISC ? 1 : -1);
+       if (dev->flags & IFF_UP) {
+               if (change & IFF_ALLMULTI)
+                       dev_set_allmulti(master,
+                                        dev->flags & IFF_ALLMULTI ? 1 : -1);
+               if (change & IFF_PROMISC)
+                       dev_set_promiscuity(master,
+                                           dev->flags & IFF_PROMISC ? 1 : -1);
+       }
 }
 
 static void dsa_slave_set_rx_mode(struct net_device *dev)
@@ -362,18 +365,23 @@ static int dsa_slave_port_obj_del(struct net_device *dev,
        return err;
 }
 
-static int dsa_slave_port_attr_get(struct net_device *dev,
-                                  struct switchdev_attr *attr)
+static int dsa_slave_get_port_parent_id(struct net_device *dev,
+                                       struct netdev_phys_item_id *ppid)
 {
        struct dsa_port *dp = dsa_slave_to_port(dev);
        struct dsa_switch *ds = dp->ds;
        struct dsa_switch_tree *dst = ds->dst;
 
+       ppid->id_len = sizeof(dst->index);
+       memcpy(&ppid->id, &dst->index, ppid->id_len);
+
+       return 0;
+}
+
+static int dsa_slave_port_attr_get(struct net_device *dev,
+                                  struct switchdev_attr *attr)
+{
        switch (attr->id) {
-       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = sizeof(dst->index);
-               memcpy(&attr->u.ppid.id, &dst->index, attr->u.ppid.id_len);
-               break;
        case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
                attr->u.brport_flags_support = 0;
                break;
@@ -639,7 +647,7 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
        int ret;
 
        /* Port's PHY and MAC both need to be EEE capable */
-       if (!dev->phydev && !dp->pl)
+       if (!dev->phydev || !dp->pl)
                return -ENODEV;
 
        if (!ds->ops->set_mac_eee)
@@ -659,7 +667,7 @@ static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
        int ret;
 
        /* Port's PHY and MAC both need to be EEE capable */
-       if (!dev->phydev && !dp->pl)
+       if (!dev->phydev || !dp->pl)
                return -ENODEV;
 
        if (!ds->ops->get_mac_eee)
@@ -1046,6 +1054,7 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
        .ndo_get_phys_port_name = dsa_slave_get_phys_port_name,
        .ndo_setup_tc           = dsa_slave_setup_tc,
        .ndo_get_stats64        = dsa_slave_get_stats64,
+       .ndo_get_port_parent_id = dsa_slave_get_port_parent_id,
 };
 
 static const struct switchdev_ops dsa_slave_switchdev_ops = {
index da71b9e..927e9c8 100644 (file)
@@ -67,6 +67,8 @@ static struct sk_buff *ksz_common_rcv(struct sk_buff *skb,
 
        pskb_trim_rcsum(skb, skb->len - len);
 
+       skb->offload_fwd_mark = true;
+
        return skb;
 }
 
index 5022bc6..8e185b5 100644 (file)
@@ -1072,7 +1072,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
                        goto failure;
        }
 
-       fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
+       fi = kzalloc(struct_size(fi, fib_nh, nhs), GFP_KERNEL);
        if (!fi)
                goto failure;
        fi->fib_metrics = ip_fib_metrics_init(fi->fib_net, cfg->fc_mx,
index a40e48d..b448cf3 100644 (file)
@@ -159,7 +159,8 @@ static int unsolicited_report_interval(struct in_device *in_dev)
        return interval_jiffies;
 }
 
-static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im);
+static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im,
+                             gfp_t gfp);
 static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im);
 static void igmpv3_clear_delrec(struct in_device *in_dev);
 static int sf_setstate(struct ip_mc_list *pmc);
@@ -1145,7 +1146,8 @@ static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr)
 /*
  * deleted ip_mc_list manipulation
  */
-static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im)
+static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im,
+                             gfp_t gfp)
 {
        struct ip_mc_list *pmc;
        struct net *net = dev_net(in_dev->dev);
@@ -1156,7 +1158,7 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im)
         * for deleted items allows change reports to use common code with
         * non-deleted or query-response MCA's.
         */
-       pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);
+       pmc = kzalloc(sizeof(*pmc), gfp);
        if (!pmc)
                return;
        spin_lock_init(&pmc->lock);
@@ -1261,7 +1263,7 @@ static void igmpv3_clear_delrec(struct in_device *in_dev)
 }
 #endif
 
-static void igmp_group_dropped(struct ip_mc_list *im)
+static void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp)
 {
        struct in_device *in_dev = im->interface;
 #ifdef CONFIG_IP_MULTICAST
@@ -1292,13 +1294,18 @@ static void igmp_group_dropped(struct ip_mc_list *im)
                        return;
                }
                /* IGMPv3 */
-               igmpv3_add_delrec(in_dev, im);
+               igmpv3_add_delrec(in_dev, im, gfp);
 
                igmp_ifc_event(in_dev);
        }
 #endif
 }
 
+static void igmp_group_dropped(struct ip_mc_list *im)
+{
+       __igmp_group_dropped(im, GFP_KERNEL);
+}
+
 static void igmp_group_added(struct ip_mc_list *im)
 {
        struct in_device *in_dev = im->interface;
@@ -1400,8 +1407,8 @@ static void ip_mc_hash_remove(struct in_device *in_dev,
 /*
  *     A socket has joined a multicast group on device dev.
  */
-static void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
-                             unsigned int mode)
+static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
+                               unsigned int mode, gfp_t gfp)
 {
        struct ip_mc_list *im;
 
@@ -1415,7 +1422,7 @@ static void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
                }
        }
 
-       im = kzalloc(sizeof(*im), GFP_KERNEL);
+       im = kzalloc(sizeof(*im), gfp);
        if (!im)
                goto out;
 
@@ -1448,6 +1455,12 @@ out:
        return;
 }
 
+void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, gfp_t gfp)
+{
+       ____ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE, gfp);
+}
+EXPORT_SYMBOL(__ip_mc_inc_group);
+
 void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
 {
        __ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE);
@@ -1634,7 +1647,7 @@ static void ip_mc_rejoin_groups(struct in_device *in_dev)
  *     A socket has left a multicast group on device dev
  */
 
-void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
+void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp)
 {
        struct ip_mc_list *i;
        struct ip_mc_list __rcu **ip;
@@ -1649,7 +1662,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
                                ip_mc_hash_remove(in_dev, i);
                                *ip = i->next_rcu;
                                in_dev->mc_count--;
-                               igmp_group_dropped(i);
+                               __igmp_group_dropped(i, gfp);
                                ip_mc_clear_src(i);
 
                                if (!in_dev->dead)
@@ -1662,7 +1675,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
                }
        }
 }
-EXPORT_SYMBOL(ip_mc_dec_group);
+EXPORT_SYMBOL(__ip_mc_dec_group);
 
 /* Device changing type */
 
index 1a4e9ff..5731670 100644 (file)
@@ -108,6 +108,7 @@ static size_t inet_sk_attr_size(struct sock *sk,
                + nla_total_size(1) /* INET_DIAG_TOS */
                + nla_total_size(1) /* INET_DIAG_TCLASS */
                + nla_total_size(4) /* INET_DIAG_MARK */
+               + nla_total_size(4) /* INET_DIAG_CLASS_ID */
                + nla_total_size(sizeof(struct inet_diag_meminfo))
                + nla_total_size(sizeof(struct inet_diag_msg))
                + nla_total_size(SK_MEMINFO_VARS * sizeof(u32))
@@ -287,12 +288,19 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
                        goto errout;
        }
 
-       if (ext & (1 << (INET_DIAG_CLASS_ID - 1))) {
+       if (ext & (1 << (INET_DIAG_CLASS_ID - 1)) ||
+           ext & (1 << (INET_DIAG_TCLASS - 1))) {
                u32 classid = 0;
 
 #ifdef CONFIG_SOCK_CGROUP_DATA
                classid = sock_cgroup_classid(&sk->sk_cgrp_data);
 #endif
+               /* Fallback to socket priority if class id isn't set.
+                * Classful qdiscs use it as direct reference to class.
+                * For cgroup2 classid is always zero.
+                */
+               if (!classid)
+                       classid = sk->sk_priority;
 
                if (nla_put_u32(skb, INET_DIAG_CLASS_ID, classid))
                        goto errout;
index d757b96..be77859 100644 (file)
@@ -216,6 +216,7 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base,
                        atomic_set(&p->rid, 0);
                        p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW;
                        p->rate_tokens = 0;
+                       p->n_redirects = 0;
                        /* 60*HZ is arbitrary, but chosen enough high so that the first
                         * calculation of tokens is at its maximum.
                         */
index d1cef66..ccee941 100644 (file)
@@ -1381,12 +1381,17 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
 {
        struct ip_tunnel *t = netdev_priv(dev);
        struct ip_tunnel_parm *p = &t->parms;
+       __be16 o_flags = p->o_flags;
+
+       if ((t->erspan_ver == 1 || t->erspan_ver == 2) &&
+           !t->collect_md)
+               o_flags |= TUNNEL_KEY;
 
        if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
            nla_put_be16(skb, IFLA_GRE_IFLAGS,
                         gre_tnl_flags_to_gre_flags(p->i_flags)) ||
            nla_put_be16(skb, IFLA_GRE_OFLAGS,
-                        gre_tnl_flags_to_gre_flags(p->o_flags)) ||
+                        gre_tnl_flags_to_gre_flags(o_flags)) ||
            nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) ||
            nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) ||
            nla_put_in_addr(skb, IFLA_GRE_LOCAL, p->iph.saddr) ||
index d7b43e7..68a21bf 100644 (file)
@@ -74,6 +74,33 @@ drop:
        return 0;
 }
 
+static int vti_input_ipip(struct sk_buff *skb, int nexthdr, __be32 spi,
+                    int encap_type)
+{
+       struct ip_tunnel *tunnel;
+       const struct iphdr *iph = ip_hdr(skb);
+       struct net *net = dev_net(skb->dev);
+       struct ip_tunnel_net *itn = net_generic(net, vti_net_id);
+
+       tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
+                                 iph->saddr, iph->daddr, 0);
+       if (tunnel) {
+               if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
+                       goto drop;
+
+               XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = tunnel;
+
+               skb->dev = tunnel->dev;
+
+               return xfrm_input(skb, nexthdr, spi, encap_type);
+       }
+
+       return -EINVAL;
+drop:
+       kfree_skb(skb);
+       return 0;
+}
+
 static int vti_rcv(struct sk_buff *skb)
 {
        XFRM_SPI_SKB_CB(skb)->family = AF_INET;
@@ -82,6 +109,14 @@ static int vti_rcv(struct sk_buff *skb)
        return vti_input(skb, ip_hdr(skb)->protocol, 0, 0);
 }
 
+static int vti_rcv_ipip(struct sk_buff *skb)
+{
+       XFRM_SPI_SKB_CB(skb)->family = AF_INET;
+       XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
+
+       return vti_input_ipip(skb, ip_hdr(skb)->protocol, ip_hdr(skb)->saddr, 0);
+}
+
 static int vti_rcv_cb(struct sk_buff *skb, int err)
 {
        unsigned short family;
@@ -435,6 +470,12 @@ static struct xfrm4_protocol vti_ipcomp4_protocol __read_mostly = {
        .priority       =       100,
 };
 
+static struct xfrm_tunnel ipip_handler __read_mostly = {
+       .handler        =       vti_rcv_ipip,
+       .err_handler    =       vti4_err,
+       .priority       =       0,
+};
+
 static int __net_init vti_init_net(struct net *net)
 {
        int err;
@@ -603,6 +644,13 @@ static int __init vti_init(void)
        if (err < 0)
                goto xfrm_proto_comp_failed;
 
+       msg = "ipip tunnel";
+       err = xfrm4_tunnel_register(&ipip_handler, AF_INET);
+       if (err < 0) {
+               pr_info("%s: cant't register tunnel\n",__func__);
+               goto xfrm_tunnel_failed;
+       }
+
        msg = "netlink interface";
        err = rtnl_link_register(&vti_link_ops);
        if (err < 0)
@@ -612,6 +660,8 @@ static int __init vti_init(void)
 
 rtnl_link_failed:
        xfrm4_protocol_deregister(&vti_ipcomp4_protocol, IPPROTO_COMP);
+xfrm_tunnel_failed:
+       xfrm4_tunnel_deregister(&ipip_handler, AF_INET);
 xfrm_proto_comp_failed:
        xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH);
 xfrm_proto_ah_failed:
index b9a9873..9bcca08 100644 (file)
@@ -85,7 +85,6 @@
 
 /* Define the friendly delay before and after opening net devices */
 #define CONF_POST_OPEN         10      /* After opening: 10 msecs */
-#define CONF_CARRIER_TIMEOUT   120000  /* Wait for carrier timeout */
 
 /* Define the timeout for waiting for a DHCP/BOOTP/RARP reply */
 #define CONF_OPEN_RETRIES      2       /* (Re)open devices twice */
 #define NONE cpu_to_be32(INADDR_NONE)
 #define ANY cpu_to_be32(INADDR_ANY)
 
+/* Wait for carrier timeout default in seconds */
+static unsigned int carrier_timeout = 120;
+
 /*
  * Public IP configuration
  */
@@ -268,9 +270,9 @@ static int __init ic_open_devs(void)
 
        /* wait for a carrier on at least one device */
        start = jiffies;
-       next_msg = start + msecs_to_jiffies(CONF_CARRIER_TIMEOUT/12);
+       next_msg = start + msecs_to_jiffies(20000);
        while (time_before(jiffies, start +
-                          msecs_to_jiffies(CONF_CARRIER_TIMEOUT))) {
+                          msecs_to_jiffies(carrier_timeout * 1000))) {
                int wait, elapsed;
 
                for_each_netdev(&init_net, dev)
@@ -283,9 +285,9 @@ static int __init ic_open_devs(void)
                        continue;
 
                elapsed = jiffies_to_msecs(jiffies - start);
-               wait = (CONF_CARRIER_TIMEOUT - elapsed + 500)/1000;
+               wait = (carrier_timeout * 1000 - elapsed + 500) / 1000;
                pr_info("Waiting up to %d more seconds for network.\n", wait);
-               next_msg = jiffies + msecs_to_jiffies(CONF_CARRIER_TIMEOUT/12);
+               next_msg = jiffies + msecs_to_jiffies(20000);
        }
 have_carrier:
        rtnl_unlock();
@@ -1780,3 +1782,18 @@ static int __init vendor_class_identifier_setup(char *addrs)
        return 1;
 }
 __setup("dhcpclass=", vendor_class_identifier_setup);
+
+static int __init set_carrier_timeout(char *str)
+{
+       ssize_t ret;
+
+       if (!str)
+               return 0;
+
+       ret = kstrtouint(str, 0, &carrier_timeout);
+       if (ret)
+               return 0;
+
+       return 1;
+}
+__setup("carrier_timeout=", set_carrier_timeout);
index fb99002..e536970 100644 (file)
@@ -67,7 +67,6 @@
 #include <net/fib_rules.h>
 #include <linux/netconf.h>
 #include <net/nexthop.h>
-#include <net/switchdev.h>
 
 #include <linux/nospec.h>
 
@@ -837,10 +836,8 @@ static void ipmr_update_thresholds(struct mr_table *mrt, struct mr_mfc *cache,
 static int vif_add(struct net *net, struct mr_table *mrt,
                   struct vifctl *vifc, int mrtsock)
 {
+       struct netdev_phys_item_id ppid = { };
        int vifi = vifc->vifc_vifi;
-       struct switchdev_attr attr = {
-               .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
-       };
        struct vif_device *v = &mrt->vif_table[vifi];
        struct net_device *dev;
        struct in_device *in_dev;
@@ -919,10 +916,10 @@ static int vif_add(struct net *net, struct mr_table *mrt,
                        vifc->vifc_flags | (!mrtsock ? VIFF_STATIC : 0),
                        (VIFF_TUNNEL | VIFF_REGISTER));
 
-       attr.orig_dev = dev;
-       if (!switchdev_port_attr_get(dev, &attr)) {
-               memcpy(v->dev_parent_id.id, attr.u.ppid.id, attr.u.ppid.id_len);
-               v->dev_parent_id.id_len = attr.u.ppid.id_len;
+       err = dev_get_port_parent_id(dev, &ppid, true);
+       if (err == 0) {
+               memcpy(v->dev_parent_id.id, ppid.id, ppid.id_len);
+               v->dev_parent_id.id_len = ppid.id_len;
        } else {
                v->dev_parent_id.id_len = 0;
        }
index 91b369b..835d50b 100644 (file)
@@ -846,9 +846,9 @@ static int clusterip_net_init(struct net *net)
 
 static void clusterip_net_exit(struct net *net)
 {
+#ifdef CONFIG_PROC_FS
        struct clusterip_net *cn = clusterip_pernet(net);
 
-#ifdef CONFIG_PROC_FS
        mutex_lock(&cn->mutex);
        proc_remove(cn->procdir);
        cn->procdir = NULL;
index e26165a..4b07eb8 100644 (file)
@@ -215,6 +215,7 @@ int nf_nat_icmp_reply_translation(struct sk_buff *skb,
 
        /* Change outer to look like the reply to an incoming packet */
        nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
+       target.dst.protonum = IPPROTO_ICMP;
        if (!nf_nat_ipv4_manip_pkt(skb, 0, &target, manip))
                return 0;
 
index a0aa13b..0a8a60c 100644 (file)
@@ -105,6 +105,8 @@ static void fast_csum(struct snmp_ctx *ctx, unsigned char offset)
 int snmp_version(void *context, size_t hdrlen, unsigned char tag,
                 const void *data, size_t datalen)
 {
+       if (datalen != 1)
+               return -EINVAL;
        if (*(unsigned char *)data > 1)
                return -ENOTSUPP;
        return 1;
@@ -114,8 +116,11 @@ int snmp_helper(void *context, size_t hdrlen, unsigned char tag,
                const void *data, size_t datalen)
 {
        struct snmp_ctx *ctx = (struct snmp_ctx *)context;
-       __be32 *pdata = (__be32 *)data;
+       __be32 *pdata;
 
+       if (datalen != 4)
+               return -EINVAL;
+       pdata = (__be32 *)data;
        if (*pdata == ctx->from) {
                pr_debug("%s: %pI4 to %pI4\n", __func__,
                         (void *)&ctx->from, (void *)&ctx->to);
index 99be68b..ecc12a7 100644 (file)
@@ -887,13 +887,15 @@ void ip_rt_send_redirect(struct sk_buff *skb)
        /* No redirected packets during ip_rt_redirect_silence;
         * reset the algorithm.
         */
-       if (time_after(jiffies, peer->rate_last + ip_rt_redirect_silence))
+       if (time_after(jiffies, peer->rate_last + ip_rt_redirect_silence)) {
                peer->rate_tokens = 0;
+               peer->n_redirects = 0;
+       }
 
        /* Too many ignored redirects; do not send anything
         * set dst.rate_last to the last seen redirected packet.
         */
-       if (peer->rate_tokens >= ip_rt_redirect_number) {
+       if (peer->n_redirects >= ip_rt_redirect_number) {
                peer->rate_last = jiffies;
                goto out_put_peer;
        }
@@ -910,6 +912,7 @@ void ip_rt_send_redirect(struct sk_buff *skb)
                icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, gw);
                peer->rate_last = jiffies;
                ++peer->rate_tokens;
+               ++peer->n_redirects;
 #ifdef CONFIG_IP_ROUTE_VERBOSE
                if (log_martians &&
                    peer->rate_tokens == ip_rt_redirect_number)
@@ -1608,7 +1611,8 @@ int ip_mc_validate_source(struct sk_buff *skb, __be32 daddr, __be32 saddr,
                return -EINVAL;
 
        if (ipv4_is_zeronet(saddr)) {
-               if (!ipv4_is_local_multicast(daddr))
+               if (!ipv4_is_local_multicast(daddr) &&
+                   ip_hdr(skb)->protocol != IPPROTO_IGMP)
                        return -EINVAL;
        } else {
                err = fib_validate_source(skb, saddr, 0, tos, 0, dev,
index 6f8d292..cab6b2f 100644 (file)
@@ -1844,57 +1844,78 @@ out:
 #endif
 
 static void tcp_update_recv_tstamps(struct sk_buff *skb,
-                                   struct scm_timestamping *tss)
+                                   struct scm_timestamping_internal *tss)
 {
        if (skb->tstamp)
-               tss->ts[0] = ktime_to_timespec(skb->tstamp);
+               tss->ts[0] = ktime_to_timespec64(skb->tstamp);
        else
-               tss->ts[0] = (struct timespec) {0};
+               tss->ts[0] = (struct timespec64) {0};
 
        if (skb_hwtstamps(skb)->hwtstamp)
-               tss->ts[2] = ktime_to_timespec(skb_hwtstamps(skb)->hwtstamp);
+               tss->ts[2] = ktime_to_timespec64(skb_hwtstamps(skb)->hwtstamp);
        else
-               tss->ts[2] = (struct timespec) {0};
+               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 *tss)
+                              struct scm_timestamping_internal *tss)
 {
-       struct timeval tv;
+       int new_tstamp = sock_flag(sk, SOCK_TSTAMP_NEW);
        bool has_timestamping = false;
 
        if (tss->ts[0].tv_sec || tss->ts[0].tv_nsec) {
                if (sock_flag(sk, SOCK_RCVTSTAMP)) {
                        if (sock_flag(sk, SOCK_RCVTSTAMPNS)) {
-                               put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS,
-                                        sizeof(tss->ts[0]), &tss->ts[0]);
-                       } else {
-                               tv.tv_sec = tss->ts[0].tv_sec;
-                               tv.tv_usec = tss->ts[0].tv_nsec / 1000;
+                               if (new_tstamp) {
+                                       struct __kernel_timespec kts = {tss->ts[0].tv_sec, tss->ts[0].tv_nsec};
+
+                                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_NEW,
+                                                sizeof(kts), &kts);
+                               } else {
+                                       struct timespec ts_old = timespec64_to_timespec(tss->ts[0]);
 
-                               put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP,
-                                        sizeof(tv), &tv);
+                                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
+                                                sizeof(ts_old), &ts_old);
+                               }
+                       } else {
+                               if (new_tstamp) {
+                                       struct __kernel_sock_timeval stv;
+
+                                       stv.tv_sec = tss->ts[0].tv_sec;
+                                       stv.tv_usec = tss->ts[0].tv_nsec / 1000;
+                                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_NEW,
+                                                sizeof(stv), &stv);
+                               } else {
+                                       struct __kernel_old_timeval tv;
+
+                                       tv.tv_sec = tss->ts[0].tv_sec;
+                                       tv.tv_usec = tss->ts[0].tv_nsec / 1000;
+                                       put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
+                                                sizeof(tv), &tv);
+                               }
                        }
                }
 
                if (sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE)
                        has_timestamping = true;
                else
-                       tss->ts[0] = (struct timespec) {0};
+                       tss->ts[0] = (struct timespec64) {0};
        }
 
        if (tss->ts[2].tv_sec || tss->ts[2].tv_nsec) {
                if (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE)
                        has_timestamping = true;
                else
-                       tss->ts[2] = (struct timespec) {0};
+                       tss->ts[2] = (struct timespec64) {0};
        }
 
        if (has_timestamping) {
-               tss->ts[1] = (struct timespec) {0};
-               put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPING,
-                        sizeof(*tss), tss);
+               tss->ts[1] = (struct timespec64) {0};
+               if (sock_flag(sk, SOCK_TSTAMP_NEW))
+                       put_cmsg_scm_timestamping64(msg, tss);
+               else
+                       put_cmsg_scm_timestamping(msg, tss);
        }
 }
 
@@ -1935,7 +1956,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
        long timeo;
        struct sk_buff *skb, *last;
        u32 urg_hole = 0;
-       struct scm_timestamping tss;
+       struct scm_timestamping_internal tss;
        bool has_tss = false;
        bool has_cmsg;
 
index dcb1d43..da5a210 100644 (file)
@@ -1200,7 +1200,8 @@ check_cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long *expires)
        list_for_each_entry(ifa, &idev->addr_list, if_list) {
                if (ifa == ifp)
                        continue;
-               if (!ipv6_prefix_equal(&ifa->addr, &ifp->addr,
+               if (ifa->prefix_len != ifp->prefix_len ||
+                   !ipv6_prefix_equal(&ifa->addr, &ifp->addr,
                                       ifp->prefix_len))
                        continue;
                if (ifa->flags & (IFA_F_PERMANENT | IFA_F_NOPREFIXROUTE))
index 5cd0029..6c79af0 100644 (file)
@@ -134,6 +134,11 @@ static int eafnosupport_ipv6_dst_lookup(struct net *net, struct sock *u1,
        return -EAFNOSUPPORT;
 }
 
+static int eafnosupport_ipv6_route_input(struct sk_buff *skb)
+{
+       return -EAFNOSUPPORT;
+}
+
 static struct fib6_table *eafnosupport_fib6_get_table(struct net *net, u32 id)
 {
        return NULL;
@@ -170,6 +175,7 @@ eafnosupport_ip6_mtu_from_fib6(struct fib6_info *f6i, struct in6_addr *daddr,
 
 const struct ipv6_stub *ipv6_stub __read_mostly = &(struct ipv6_stub) {
        .ipv6_dst_lookup   = eafnosupport_ipv6_dst_lookup,
+       .ipv6_route_input  = eafnosupport_ipv6_route_input,
        .fib6_get_table    = eafnosupport_fib6_get_table,
        .fib6_table_lookup = eafnosupport_fib6_table_lookup,
        .fib6_lookup       = eafnosupport_fib6_lookup,
index d99753b..2f45d2a 100644 (file)
@@ -900,10 +900,17 @@ static struct pernet_operations inet6_net_ops = {
        .exit = inet6_net_exit,
 };
 
+static int ipv6_route_input(struct sk_buff *skb)
+{
+       ip6_route_input(skb);
+       return skb_dst(skb)->error;
+}
+
 static const struct ipv6_stub ipv6_stub_impl = {
        .ipv6_sock_mc_join = ipv6_sock_mc_join,
        .ipv6_sock_mc_drop = ipv6_sock_mc_drop,
        .ipv6_dst_lookup   = ip6_dst_lookup,
+       .ipv6_route_input  = ipv6_route_input,
        .fib6_get_table    = fib6_get_table,
        .fib6_table_lookup = fib6_table_lookup,
        .fib6_lookup       = fib6_lookup,
index e081e69..65a4f96 100644 (file)
@@ -2098,12 +2098,17 @@ static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
 {
        struct ip6_tnl *t = netdev_priv(dev);
        struct __ip6_tnl_parm *p = &t->parms;
+       __be16 o_flags = p->o_flags;
+
+       if ((p->erspan_ver == 1 || p->erspan_ver == 2) &&
+           !p->collect_md)
+               o_flags |= TUNNEL_KEY;
 
        if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
            nla_put_be16(skb, IFLA_GRE_IFLAGS,
                         gre_tnl_flags_to_gre_flags(p->i_flags)) ||
            nla_put_be16(skb, IFLA_GRE_OFLAGS,
-                        gre_tnl_flags_to_gre_flags(p->o_flags)) ||
+                        gre_tnl_flags_to_gre_flags(o_flags)) ||
            nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) ||
            nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) ||
            nla_put_in6_addr(skb, IFLA_GRE_LOCAL, &p->laddr) ||
index 30337b3..cc01aa3 100644 (file)
@@ -1516,6 +1516,9 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all)
                        continue;
                rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params);
                list_del_rcu(&c->list);
+               call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
+                                              FIB_EVENT_ENTRY_DEL,
+                                              (struct mfc6_cache *)c, mrt->id);
                mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
                mr_cache_put(c);
        }
@@ -1524,10 +1527,6 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all)
                spin_lock_bh(&mfc_unres_lock);
                list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
                        list_del(&c->list);
-                       call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
-                                                      FIB_EVENT_ENTRY_DEL,
-                                                      (struct mfc6_cache *)c,
-                                                      mrt->id);
                        mr6_netlink_event(mrt, (struct mfc6_cache *)c,
                                          RTM_DELROUTE);
                        ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c);
index a826303..1240ccd 100644 (file)
@@ -23,9 +23,11 @@ int ip6_route_me_harder(struct net *net, struct sk_buff *skb)
        struct sock *sk = sk_to_full_sk(skb->sk);
        unsigned int hh_len;
        struct dst_entry *dst;
+       int strict = (ipv6_addr_type(&iph->daddr) &
+                     (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL));
        struct flowi6 fl6 = {
                .flowi6_oif = sk && sk->sk_bound_dev_if ? sk->sk_bound_dev_if :
-                       rt6_need_strict(&iph->daddr) ? skb_dst(skb)->dev->ifindex : 0,
+                       strict ? skb_dst(skb)->dev->ifindex : 0,
                .flowi6_mark = skb->mark,
                .flowi6_uid = sock_net_uid(net, sk),
                .daddr = iph->daddr,
index b52026a..490bfd3 100644 (file)
@@ -227,6 +227,7 @@ int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
        }
 
        nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
+       target.dst.protonum = IPPROTO_ICMPV6;
        if (!nf_nat_ipv6_manip_pkt(skb, 0, &target, manip))
                return 0;
 
index dc066fd..87a0561 100644 (file)
@@ -2277,14 +2277,8 @@ static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
 
 static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
 {
-       bool from_set;
-
-       rcu_read_lock();
-       from_set = !!rcu_dereference(rt->from);
-       rcu_read_unlock();
-
        return !(rt->rt6i_flags & RTF_CACHE) &&
-               (rt->rt6i_flags & RTF_PCPU || from_set);
+               (rt->rt6i_flags & RTF_PCPU || rcu_access_pointer(rt->from));
 }
 
 static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
index 8d0ba75..9b2f272 100644 (file)
@@ -221,9 +221,7 @@ static int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info)
        rcu_read_unlock();
 
        genlmsg_end(msg, hdr);
-       genlmsg_reply(msg, info);
-
-       return 0;
+       return genlmsg_reply(msg, info);
 
 nla_put_failure:
        rcu_read_unlock();
index 8181ee7..ee5403c 100644 (file)
@@ -146,6 +146,8 @@ int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
        } else {
                ip6_flow_hdr(hdr, 0, flowlabel);
                hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb));
+
+               memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
        }
 
        hdr->nexthdr = NEXTHDR_ROUTING;
index 1e03305..e8a1dab 100644 (file)
@@ -546,7 +546,8 @@ static int ipip6_err(struct sk_buff *skb, u32 info)
        }
 
        err = 0;
-       if (!ip6_err_gen_icmpv6_unreach(skb, iph->ihl * 4, type, data_len))
+       if (__in6_dev_get(skb->dev) &&
+           !ip6_err_gen_icmpv6_unreach(skb, iph->ihl * 4, type, data_len))
                goto out;
 
        if (t->parms.iph.daddr == 0)
index 26f1d43..fed6bec 100644 (file)
@@ -83,8 +83,7 @@
 #define L2TP_SLFLAG_S     0x40000000
 #define L2TP_SL_SEQ_MASK   0x00ffffff
 
-#define L2TP_HDR_SIZE_SEQ              10
-#define L2TP_HDR_SIZE_NOSEQ            6
+#define L2TP_HDR_SIZE_MAX              14
 
 /* Default trace flags */
 #define L2TP_DEFAULT_DEBUG_FLAGS       0
@@ -808,7 +807,7 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
        __skb_pull(skb, sizeof(struct udphdr));
 
        /* Short packet? */
-       if (!pskb_may_pull(skb, L2TP_HDR_SIZE_SEQ)) {
+       if (!pskb_may_pull(skb, L2TP_HDR_SIZE_MAX)) {
                l2tp_info(tunnel, L2TP_MSG_DATA,
                          "%s: recv short packet (len=%d)\n",
                          tunnel->name, skb->len);
@@ -884,6 +883,10 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
                goto error;
        }
 
+       if (tunnel->version == L2TP_HDR_VER_3 &&
+           l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr))
+               goto error;
+
        l2tp_recv_common(session, skb, ptr, optr, hdrflags, length);
        l2tp_session_dec_refcount(session);
 
index 9c9afe9..b2ce902 100644 (file)
@@ -301,6 +301,26 @@ static inline bool l2tp_tunnel_uses_xfrm(const struct l2tp_tunnel *tunnel)
 }
 #endif
 
+static inline int l2tp_v3_ensure_opt_in_linear(struct l2tp_session *session, struct sk_buff *skb,
+                                              unsigned char **ptr, unsigned char **optr)
+{
+       int opt_len = session->peer_cookie_len + l2tp_get_l2specific_len(session);
+
+       if (opt_len > 0) {
+               int off = *ptr - *optr;
+
+               if (!pskb_may_pull(skb, off + opt_len))
+                       return -1;
+
+               if (skb->data != *optr) {
+                       *optr = skb->data;
+                       *ptr = skb->data + off;
+               }
+       }
+
+       return 0;
+}
+
 #define l2tp_printk(ptr, type, func, fmt, ...)                         \
 do {                                                                   \
        if (((ptr)->debug) & (type))                                    \
index 35f6f86..d4c6052 100644 (file)
@@ -165,6 +165,9 @@ static int l2tp_ip_recv(struct sk_buff *skb)
                print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, ptr, length);
        }
 
+       if (l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr))
+               goto discard_sess;
+
        l2tp_recv_common(session, skb, ptr, optr, 0, skb->len);
        l2tp_session_dec_refcount(session);
 
index 237f1a4..0ae6899 100644 (file)
@@ -178,6 +178,9 @@ static int l2tp_ip6_recv(struct sk_buff *skb)
                print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, ptr, length);
        }
 
+       if (l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr))
+               goto discard_sess;
+
        l2tp_recv_common(session, skb, ptr, optr, 0, skb->len);
        l2tp_session_dec_refcount(session);
 
index 69e831b..2c4cd41 100644 (file)
@@ -8,7 +8,7 @@
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2007-2010, Intel Corporation
  * Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018 Intel Corporation
+ * Copyright (C) 2018 - 2019 Intel Corporation
  *
  * 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
@@ -229,7 +229,7 @@ ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable)
        clear_bit(IEEE80211_TXQ_STOP, &txqi->flags);
        local_bh_disable();
        rcu_read_lock();
-       drv_wake_tx_queue(sta->sdata->local, txqi);
+       schedule_and_wake_txq(sta->sdata->local, txqi);
        rcu_read_unlock();
        local_bh_enable();
 }
@@ -366,6 +366,8 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
 
        set_bit(HT_AGG_STATE_STOPPING, &tid_tx->state);
 
+       ieee80211_agg_stop_txq(sta, tid);
+
        spin_unlock_bh(&sta->lock);
 
        ht_dbg(sta->sdata, "Tx BA session stop requested for %pM tid %u\n",
index 2493c74..d65aa01 100644 (file)
@@ -1231,6 +1231,11 @@ static void sta_apply_mesh_params(struct ieee80211_local *local,
                        ieee80211_mps_sta_status_update(sta);
                        changed |= ieee80211_mps_set_sta_local_pm(sta,
                                      sdata->u.mesh.mshcfg.power_mode);
+
+                       ewma_mesh_tx_rate_avg_init(&sta->mesh->tx_rate_avg);
+                       /* init at low value */
+                       ewma_mesh_tx_rate_avg_add(&sta->mesh->tx_rate_avg, 10);
+
                        break;
                case NL80211_PLINK_LISTEN:
                case NL80211_PLINK_BLOCKED:
@@ -1447,6 +1452,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
        if (ieee80211_vif_is_mesh(&sdata->vif))
                sta_apply_mesh_params(local, sta, params);
 
+       if (params->airtime_weight)
+               sta->airtime_weight = params->airtime_weight;
+
        /* set the STA state after all sta info from usermode has been set */
        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
            set & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
@@ -1746,7 +1754,9 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
                        MPATH_INFO_EXPTIME |
                        MPATH_INFO_DISCOVERY_TIMEOUT |
                        MPATH_INFO_DISCOVERY_RETRIES |
-                       MPATH_INFO_FLAGS;
+                       MPATH_INFO_FLAGS |
+                       MPATH_INFO_HOP_COUNT |
+                       MPATH_INFO_PATH_CHANGE;
 
        pinfo->frame_qlen = mpath->frame_queue.qlen;
        pinfo->sn = mpath->sn;
@@ -1766,6 +1776,8 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
                pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
        if (mpath->flags & MESH_PATH_RESOLVED)
                pinfo->flags |= NL80211_MPATH_FLAG_RESOLVED;
+       pinfo->hop_count = mpath->hop_count;
+       pinfo->path_change_count = mpath->path_change_count;
 }
 
 static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
index 3fe541e..343ad0a 100644 (file)
@@ -218,6 +218,7 @@ static const char *hw_flag_names[] = {
        FLAG(BUFF_MMPDU_TXQ),
        FLAG(SUPPORTS_VHT_EXT_NSS_BW),
        FLAG(STA_MMPDU_TXQ),
+       FLAG(TX_STATUS_NO_AMPDU_LEN),
 #undef FLAG
 };
 
@@ -383,6 +384,9 @@ void debugfs_hw_add(struct ieee80211_local *local)
        if (local->ops->wake_tx_queue)
                DEBUGFS_ADD_MODE(aqm, 0600);
 
+       debugfs_create_u16("airtime_flags", 0600,
+                          phyd, &local->airtime_flags);
+
        statsd = debugfs_create_dir("statistics", phyd);
 
        /* if the dir failed, don't put all the other things into the root! */
index b753194..3aa618d 100644 (file)
@@ -181,9 +181,9 @@ static ssize_t sta_aqm_read(struct file *file, char __user *userbuf,
                               txqi->tin.tx_bytes,
                               txqi->tin.tx_packets,
                               txqi->flags,
-                              txqi->flags & (1<<IEEE80211_TXQ_STOP) ? "STOP" : "RUN",
-                              txqi->flags & (1<<IEEE80211_TXQ_AMPDU) ? " AMPDU" : "",
-                              txqi->flags & (1<<IEEE80211_TXQ_NO_AMSDU) ? " NO-AMSDU" : "");
+                              test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ? "STOP" : "RUN",
+                              test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags) ? " AMPDU" : "",
+                              test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags) ? " NO-AMSDU" : "");
        }
 
        rcu_read_unlock();
@@ -195,6 +195,64 @@ static ssize_t sta_aqm_read(struct file *file, char __user *userbuf,
 }
 STA_OPS(aqm);
 
+static ssize_t sta_airtime_read(struct file *file, char __user *userbuf,
+                               size_t count, loff_t *ppos)
+{
+       struct sta_info *sta = file->private_data;
+       struct ieee80211_local *local = sta->sdata->local;
+       size_t bufsz = 200;
+       char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
+       u64 rx_airtime = 0, tx_airtime = 0;
+       s64 deficit[IEEE80211_NUM_ACS];
+       ssize_t rv;
+       int ac;
+
+       if (!buf)
+               return -ENOMEM;
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               spin_lock_bh(&local->active_txq_lock[ac]);
+               rx_airtime += sta->airtime[ac].rx_airtime;
+               tx_airtime += sta->airtime[ac].tx_airtime;
+               deficit[ac] = sta->airtime[ac].deficit;
+               spin_unlock_bh(&local->active_txq_lock[ac]);
+       }
+
+       p += scnprintf(p, bufsz + buf - p,
+               "RX: %llu us\nTX: %llu us\nWeight: %u\n"
+               "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
+               rx_airtime,
+               tx_airtime,
+               sta->airtime_weight,
+               deficit[0],
+               deficit[1],
+               deficit[2],
+               deficit[3]);
+
+       rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+       kfree(buf);
+       return rv;
+}
+
+static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf,
+                                size_t count, loff_t *ppos)
+{
+       struct sta_info *sta = file->private_data;
+       struct ieee80211_local *local = sta->sdata->local;
+       int ac;
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               spin_lock_bh(&local->active_txq_lock[ac]);
+               sta->airtime[ac].rx_airtime = 0;
+               sta->airtime[ac].tx_airtime = 0;
+               sta->airtime[ac].deficit = sta->airtime_weight;
+               spin_unlock_bh(&local->active_txq_lock[ac]);
+       }
+
+       return count;
+}
+STA_OPS_RW(airtime);
+
 static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
                                        size_t count, loff_t *ppos)
 {
@@ -906,6 +964,10 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
        if (local->ops->wake_tx_queue)
                DEBUGFS_ADD(aqm);
 
+       if (wiphy_ext_feature_isset(local->hw.wiphy,
+                                   NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+               DEBUGFS_ADD(airtime);
+
        if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
                debugfs_create_x32("driver_buffered_tids", 0400,
                                   sta->debugfs_dir,
index 3e0d592..ba3c07b 100644 (file)
@@ -1173,6 +1173,13 @@ static inline void drv_wake_tx_queue(struct ieee80211_local *local,
        local->ops->wake_tx_queue(&local->hw, &txq->txq);
 }
 
+static inline void schedule_and_wake_txq(struct ieee80211_local *local,
+                                        struct txq_info *txqi)
+{
+       ieee80211_schedule_txq(&local->hw, &txqi->txq);
+       drv_wake_tx_queue(local, txqi);
+}
+
 static inline int drv_can_aggregate_in_amsdu(struct ieee80211_local *local,
                                             struct sk_buff *head,
                                             struct sk_buff *skb)
index f849ea8..e03c46a 100644 (file)
@@ -107,6 +107,14 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap,
                             IEEE80211_HT_CAP_40MHZ_INTOLERANT);
 
+       /* Allow user to enable TX STBC bit  */
+       __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap,
+                            IEEE80211_HT_CAP_TX_STBC);
+
+       /* Allow user to configure RX STBC bits */
+       if (ht_capa_mask->cap_info & IEEE80211_HT_CAP_RX_STBC)
+               ht_cap->cap |= ht_capa->cap_info & IEEE80211_HT_CAP_RX_STBC;
+
        /* Allow user to decrease AMPDU factor */
        if (ht_capa_mask->ampdu_params_info &
            IEEE80211_HT_AMPDU_PARM_FACTOR) {
index 7dfb4e2..056b16b 100644 (file)
@@ -831,6 +831,8 @@ enum txq_info_flags {
  *     a fq_flow which is already owned by a different tin
  * @def_cvars: codel vars for @def_flow
  * @frags: used to keep fragments created after dequeue
+ * @schedule_order: used with ieee80211_local->active_txqs
+ * @schedule_round: counter to prevent infinite loops on TXQ scheduling
  */
 struct txq_info {
        struct fq_tin tin;
@@ -838,6 +840,8 @@ struct txq_info {
        struct codel_vars def_cvars;
        struct codel_stats cstats;
        struct sk_buff_head frags;
+       struct list_head schedule_order;
+       u16 schedule_round;
        unsigned long flags;
 
        /* keep last! */
@@ -1129,6 +1133,13 @@ struct ieee80211_local {
        struct codel_vars *cvars;
        struct codel_params cparams;
 
+       /* protects active_txqs and txqi->schedule_order */
+       spinlock_t active_txq_lock[IEEE80211_NUM_ACS];
+       struct list_head active_txqs[IEEE80211_NUM_ACS];
+       u16 schedule_round[IEEE80211_NUM_ACS];
+
+       u16 airtime_flags;
+
        const struct ieee80211_ops *ops;
 
        /*
index 87a7299..71005b6 100644 (file)
@@ -478,6 +478,8 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
                                IEEE80211_HT_CAP_MAX_AMSDU |
                                IEEE80211_HT_CAP_SGI_20 |
                                IEEE80211_HT_CAP_SGI_40 |
+                               IEEE80211_HT_CAP_TX_STBC |
+                               IEEE80211_HT_CAP_RX_STBC |
                                IEEE80211_HT_CAP_LDPC_CODING |
                                IEEE80211_HT_CAP_40MHZ_INTOLERANT),
        .mcs = {
@@ -663,6 +665,12 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
        spin_lock_init(&local->rx_path_lock);
        spin_lock_init(&local->queue_stop_reason_lock);
 
+       for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+               INIT_LIST_HEAD(&local->active_txqs[i]);
+               spin_lock_init(&local->active_txq_lock[i]);
+       }
+       local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX;
+
        INIT_LIST_HEAD(&local->chanctx_list);
        mutex_init(&local->chanctx_mtx);
 
@@ -1148,6 +1156,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        if (!local->hw.max_nan_de_entries)
                local->hw.max_nan_de_entries = IEEE80211_MAX_NAN_INSTANCE_ID;
 
+       if (!local->hw.weight_multiplier)
+               local->hw.weight_multiplier = 1;
+
        result = ieee80211_wep_init(local);
        if (result < 0)
                wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",
index cad6592..8b26858 100644 (file)
@@ -94,6 +94,7 @@ enum mesh_deferred_task_flags {
  * @last_preq_to_root: Timestamp of last PREQ sent to root
  * @is_root: the destination station of this path is a root node
  * @is_gate: the destination station of this path is a mesh gate
+ * @path_change_count: the number of path changes to destination
  *
  *
  * The dst address is unique in the mesh path table. Since the mesh_path is
@@ -124,6 +125,7 @@ struct mesh_path {
        unsigned long last_preq_to_root;
        bool is_root;
        bool is_gate;
+       u32 path_change_count;
 };
 
 /**
index 6950cd0..e00284a 100644 (file)
@@ -300,6 +300,7 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
 {
        struct ieee80211_tx_info *txinfo = st->info;
        int failed;
+       struct rate_info rinfo;
 
        failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);
 
@@ -310,12 +311,15 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
        if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) >
                        LINK_FAIL_THRESH)
                mesh_plink_broken(sta);
+
+       sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo);
+       ewma_mesh_tx_rate_avg_add(&sta->mesh->tx_rate_avg,
+                                 cfg80211_calculate_bitrate(&rinfo));
 }
 
 static u32 airtime_link_metric_get(struct ieee80211_local *local,
                                   struct sta_info *sta)
 {
-       struct rate_info rinfo;
        /* This should be adjusted for each device */
        int device_constant = 1 << ARITH_SHIFT;
        int test_frame_len = TEST_FRAME_LEN << ARITH_SHIFT;
@@ -339,8 +343,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
                if (fail_avg > LINK_FAIL_THRESH)
                        return MAX_METRIC;
 
-               sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo);
-               rate = cfg80211_calculate_bitrate(&rinfo);
+               rate = ewma_mesh_tx_rate_avg_read(&sta->mesh->tx_rate_avg);
                if (WARN_ON(!rate))
                        return MAX_METRIC;
 
@@ -386,6 +389,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
        unsigned long orig_lifetime, exp_time;
        u32 last_hop_metric, new_metric;
        bool process = true;
+       u8 hopcount;
 
        rcu_read_lock();
        sta = sta_info_get(sdata, mgmt->sa);
@@ -404,6 +408,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
                orig_sn = PREQ_IE_ORIG_SN(hwmp_ie);
                orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie);
                orig_metric = PREQ_IE_METRIC(hwmp_ie);
+               hopcount = PREQ_IE_HOPCOUNT(hwmp_ie) + 1;
                break;
        case MPATH_PREP:
                /* Originator here refers to the MP that was the target in the
@@ -415,6 +420,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
                orig_sn = PREP_IE_TARGET_SN(hwmp_ie);
                orig_lifetime = PREP_IE_LIFETIME(hwmp_ie);
                orig_metric = PREP_IE_METRIC(hwmp_ie);
+               hopcount = PREP_IE_HOPCOUNT(hwmp_ie) + 1;
                break;
        default:
                rcu_read_unlock();
@@ -441,7 +447,10 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
                            (mpath->flags & MESH_PATH_SN_VALID)) {
                                if (SN_GT(mpath->sn, orig_sn) ||
                                    (mpath->sn == orig_sn &&
-                                    new_metric >= mpath->metric)) {
+                                    (rcu_access_pointer(mpath->next_hop) !=
+                                                     sta ?
+                                             mult_frac(new_metric, 10, 9) :
+                                             new_metric) >= mpath->metric)) {
                                        process = false;
                                        fresh_info = false;
                                }
@@ -476,12 +485,15 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
                }
 
                if (fresh_info) {
+                       if (rcu_access_pointer(mpath->next_hop) != sta)
+                               mpath->path_change_count++;
                        mesh_path_assign_nexthop(mpath, sta);
                        mpath->flags |= MESH_PATH_SN_VALID;
                        mpath->metric = new_metric;
                        mpath->sn = orig_sn;
                        mpath->exp_time = time_after(mpath->exp_time, exp_time)
                                          ?  mpath->exp_time : exp_time;
+                       mpath->hop_count = hopcount;
                        mesh_path_activate(mpath);
                        spin_unlock_bh(&mpath->state_lock);
                        ewma_mesh_fail_avg_init(&sta->mesh->fail_avg);
@@ -506,8 +518,10 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
                if (mpath) {
                        spin_lock_bh(&mpath->state_lock);
                        if ((mpath->flags & MESH_PATH_FIXED) ||
-                               ((mpath->flags & MESH_PATH_ACTIVE) &&
-                                       (last_hop_metric > mpath->metric)))
+                           ((mpath->flags & MESH_PATH_ACTIVE) &&
+                            ((rcu_access_pointer(mpath->next_hop) != sta ?
+                                      mult_frac(last_hop_metric, 10, 9) :
+                                      last_hop_metric) > mpath->metric)))
                                fresh_info = false;
                } else {
                        mpath = mesh_path_add(sdata, ta);
@@ -519,10 +533,13 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
                }
 
                if (fresh_info) {
+                       if (rcu_access_pointer(mpath->next_hop) != sta)
+                               mpath->path_change_count++;
                        mesh_path_assign_nexthop(mpath, sta);
                        mpath->metric = last_hop_metric;
                        mpath->exp_time = time_after(mpath->exp_time, exp_time)
                                          ?  mpath->exp_time : exp_time;
+                       mpath->hop_count = 1;
                        mesh_path_activate(mpath);
                        spin_unlock_bh(&mpath->state_lock);
                        ewma_mesh_fail_avg_init(&sta->mesh->fail_avg);
index f466ec3..ccaf951 100644 (file)
@@ -294,6 +294,15 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index)
        return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES];
 }
 
+static unsigned int
+minstrel_ht_avg_ampdu_len(struct minstrel_ht_sta *mi)
+{
+       if (!mi->avg_ampdu_len)
+               return AVG_AMPDU_SIZE;
+
+       return MINSTREL_TRUNC(mi->avg_ampdu_len);
+}
+
 /*
  * Return current throughput based on the average A-MPDU length, taking into
  * account the expected number of retransmissions and their expected length
@@ -309,7 +318,7 @@ minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
                return 0;
 
        if (group != MINSTREL_CCK_GROUP)
-               nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
+               nsecs = 1000 * mi->overhead / minstrel_ht_avg_ampdu_len(mi);
 
        nsecs += minstrel_mcs_groups[group].duration[rate] <<
                 minstrel_mcs_groups[group].shift;
@@ -503,8 +512,12 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
        u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
 
        if (mi->ampdu_packets > 0) {
-               mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
-                       MINSTREL_FRAC(mi->ampdu_len, mi->ampdu_packets), EWMA_LEVEL);
+               if (!ieee80211_hw_check(mp->hw, TX_STATUS_NO_AMPDU_LEN))
+                       mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
+                               MINSTREL_FRAC(mi->ampdu_len, mi->ampdu_packets),
+                                             EWMA_LEVEL);
+               else
+                       mi->avg_ampdu_len = 0;
                mi->ampdu_len = 0;
                mi->ampdu_packets = 0;
        }
@@ -709,7 +722,9 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
        mi->ampdu_len += info->status.ampdu_len;
 
        if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) {
-               mi->sample_wait = 16 + 2 * MINSTREL_TRUNC(mi->avg_ampdu_len);
+               int avg_ampdu_len = minstrel_ht_avg_ampdu_len(mi);
+
+               mi->sample_wait = 16 + 2 * avg_ampdu_len;
                mi->sample_tries = 1;
                mi->sample_count--;
        }
@@ -777,7 +792,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        unsigned int cw = mp->cw_min;
        unsigned int ctime = 0;
        unsigned int t_slot = 9; /* FIXME */
-       unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len);
+       unsigned int ampdu_len = minstrel_ht_avg_ampdu_len(mi);
        unsigned int overhead = 0, overhead_rtscts = 0;
 
        mrs = minstrel_get_ratestats(mi, index);
index 57820a5..31641d0 100644 (file)
@@ -160,9 +160,10 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
                        "lookaround %d\n",
                        max(0, (int) mi->total_packets - (int) mi->sample_packets),
                        mi->sample_packets);
-       p += sprintf(p, "Average # of aggregated frames per A-MPDU: %d.%d\n",
-               MINSTREL_TRUNC(mi->avg_ampdu_len),
-               MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
+       if (mi->avg_ampdu_len)
+               p += sprintf(p, "Average # of aggregated frames per A-MPDU: %d.%d\n",
+                       MINSTREL_TRUNC(mi->avg_ampdu_len),
+                       MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
        ms->len = p - ms->buf;
        WARN_ON(ms->len + sizeof(*ms) > 32768);
 
index c4a8f11..11f0589 100644 (file)
@@ -90,7 +90,6 @@ static void __cleanup_single_sta(struct sta_info *sta)
        struct tid_ampdu_tx *tid_tx;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
-       struct fq *fq = &local->fq;
        struct ps_data *ps;
 
        if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
@@ -120,9 +119,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
 
                        txqi = to_txq_info(sta->sta.txq[i]);
 
-                       spin_lock_bh(&fq->lock);
                        ieee80211_txq_purge(local, txqi);
-                       spin_unlock_bh(&fq->lock);
                }
        }
 
@@ -387,9 +384,12 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        if (sta_prepare_rate_control(local, sta, gfp))
                goto free_txq;
 
+       sta->airtime_weight = IEEE80211_DEFAULT_AIRTIME_WEIGHT;
+
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                skb_queue_head_init(&sta->ps_tx_buf[i]);
                skb_queue_head_init(&sta->tx_filtered[i]);
+               sta->airtime[i].deficit = sta->airtime_weight;
        }
 
        for (i = 0; i < IEEE80211_NUM_TIDS; i++)
@@ -1249,7 +1249,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
                if (!sta->sta.txq[i] || !txq_has_queue(sta->sta.txq[i]))
                        continue;
 
-               drv_wake_tx_queue(local, to_txq_info(sta->sta.txq[i]));
+               schedule_and_wake_txq(local, to_txq_info(sta->sta.txq[i]));
        }
 
        skb_queue_head_init(&pending);
@@ -1826,6 +1826,27 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
 }
 EXPORT_SYMBOL(ieee80211_sta_set_buffered);
 
+void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
+                                   u32 tx_airtime, u32 rx_airtime)
+{
+       struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+       struct ieee80211_local *local = sta->sdata->local;
+       u8 ac = ieee80211_ac_from_tid(tid);
+       u32 airtime = 0;
+
+       if (sta->local->airtime_flags & AIRTIME_USE_TX)
+               airtime += tx_airtime;
+       if (sta->local->airtime_flags & AIRTIME_USE_RX)
+               airtime += rx_airtime;
+
+       spin_lock_bh(&local->active_txq_lock[ac]);
+       sta->airtime[ac].tx_airtime += tx_airtime;
+       sta->airtime[ac].rx_airtime += rx_airtime;
+       sta->airtime[ac].deficit -= airtime;
+       spin_unlock_bh(&local->active_txq_lock[ac]);
+}
+EXPORT_SYMBOL(ieee80211_sta_register_airtime);
+
 int sta_info_move_state(struct sta_info *sta,
                        enum ieee80211_sta_state new_state)
 {
@@ -2188,6 +2209,23 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
                sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
        }
 
+       if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_DURATION))) {
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       sinfo->rx_duration += sta->airtime[ac].rx_airtime;
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION);
+       }
+
+       if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_DURATION))) {
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       sinfo->tx_duration += sta->airtime[ac].tx_airtime;
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_DURATION);
+       }
+
+       if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT))) {
+               sinfo->airtime_weight = sta->airtime_weight;
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT);
+       }
+
        sinfo->rx_dropped_misc = sta->rx_stats.dropped;
        if (sta->pcpu_rx_stats) {
                for_each_possible_cpu(cpu) {
index 8eb2904..71f7e49 100644 (file)
@@ -127,6 +127,16 @@ enum ieee80211_agg_stop_reason {
        AGG_STOP_DESTROY_STA,
 };
 
+/* Debugfs flags to enable/disable use of RX/TX airtime in scheduler */
+#define AIRTIME_USE_TX         BIT(0)
+#define AIRTIME_USE_RX         BIT(1)
+
+struct airtime_info {
+       u64 rx_airtime;
+       u64 tx_airtime;
+       s64 deficit;
+};
+
 struct sta_info;
 
 /**
@@ -343,6 +353,7 @@ struct ieee80211_fast_rx {
 
 /* we use only values in the range 0-100, so pick a large precision */
 DECLARE_EWMA(mesh_fail_avg, 20, 8)
+DECLARE_EWMA(mesh_tx_rate_avg, 8, 16)
 
 /**
  * struct mesh_sta - mesh STA information
@@ -366,6 +377,7 @@ DECLARE_EWMA(mesh_fail_avg, 20, 8)
  *     processed
  * @connected_to_gate: true if mesh STA has a path to a mesh gate
  * @fail_avg: moving percentage of failed MSDUs
+ * @tx_rate_avg: moving average of tx bitrate
  */
 struct mesh_sta {
        struct timer_list plink_timer;
@@ -394,6 +406,8 @@ struct mesh_sta {
 
        /* moving percentage of failed MSDUs */
        struct ewma_mesh_fail_avg fail_avg;
+       /* moving average of tx bitrate */
+       struct ewma_mesh_tx_rate_avg tx_rate_avg;
 };
 
 DECLARE_EWMA(signal, 10, 8)
@@ -459,6 +473,9 @@ struct ieee80211_sta_rx_stats {
  * @last_seq_ctrl: last received seq/frag number from this STA (per TID
  *     plus one for non-QoS frames)
  * @tid_seq: per-TID sequence numbers for sending to this STA
+ * @airtime: per-AC struct airtime_info describing airtime statistics for this
+ *     station
+ * @airtime_weight: station weight for airtime fairness calculation purposes
  * @ampdu_mlme: A-MPDU state machine state
  * @mesh: mesh STA information
  * @debugfs_dir: debug filesystem directory dentry
@@ -480,10 +497,28 @@ struct ieee80211_sta_rx_stats {
  * @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
  *     the BSS one.
  * @tx_stats: TX statistics
+ * @tx_stats.packets: # of packets transmitted
+ * @tx_stats.bytes: # of bytes in all packets transmitted
+ * @tx_stats.last_rate: last TX rate
+ * @tx_stats.msdu: # of transmitted MSDUs per TID
  * @rx_stats: RX statistics
+ * @rx_stats_avg: averaged RX statistics
+ * @rx_stats_avg.signal: averaged signal
+ * @rx_stats_avg.chain_signal: averaged per-chain signal
  * @pcpu_rx_stats: per-CPU RX statistics, assigned only if the driver needs
  *     this (by advertising the USES_RSS hw flag)
  * @status_stats: TX status statistics
+ * @status_stats.filtered: # of filtered frames
+ * @status_stats.retry_failed: # of frames that failed after retry
+ * @status_stats.retry_count: # of retries attempted
+ * @status_stats.lost_packets: # of lost packets
+ * @status_stats.last_tdls_pkt_time: timestamp of last TDLS packet
+ * @status_stats.msdu_retries: # of MSDU retries
+ * @status_stats.msdu_failed: # of failed MSDUs
+ * @status_stats.last_ack: last ack timestamp (jiffies)
+ * @status_stats.last_ack_signal: last ACK signal
+ * @status_stats.ack_signal_filled: last ACK signal validity
+ * @status_stats.avg_ack_signal: average ACK signal
  */
 struct sta_info {
        /* General information, mostly static */
@@ -565,6 +600,9 @@ struct sta_info {
        } tx_stats;
        u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
 
+       struct airtime_info airtime[IEEE80211_NUM_ACS];
+       u16 airtime_weight;
+
        /*
         * Aggregation information, locked with lock.
         */
index 3f0b96e..5b9952b 100644 (file)
@@ -823,6 +823,12 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
                        ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
                                                acked, info->status.tx_time);
 
+               if (info->status.tx_time &&
+                   wiphy_ext_feature_isset(local->hw.wiphy,
+                                           NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+                       ieee80211_sta_register_airtime(&sta->sta, tid,
+                                                      info->status.tx_time, 0);
+
                if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
                        if (info->flags & IEEE80211_TX_STAT_ACK) {
                                if (sta->status_stats.lost_packets)
index f170d6c..8a49a74 100644 (file)
@@ -1449,6 +1449,7 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
        codel_vars_init(&txqi->def_cvars);
        codel_stats_init(&txqi->cstats);
        __skb_queue_head_init(&txqi->frags);
+       INIT_LIST_HEAD(&txqi->schedule_order);
 
        txqi->txq.vif = &sdata->vif;
 
@@ -1487,8 +1488,14 @@ void ieee80211_txq_purge(struct ieee80211_local *local,
        struct fq *fq = &local->fq;
        struct fq_tin *tin = &txqi->tin;
 
+       spin_lock_bh(&fq->lock);
        fq_tin_reset(fq, tin, fq_skb_free_func);
        ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
+       spin_unlock_bh(&fq->lock);
+
+       spin_lock_bh(&local->active_txq_lock[txqi->txq.ac]);
+       list_del_init(&txqi->schedule_order);
+       spin_unlock_bh(&local->active_txq_lock[txqi->txq.ac]);
 }
 
 void ieee80211_txq_set_params(struct ieee80211_local *local)
@@ -1605,7 +1612,7 @@ static bool ieee80211_queue_skb(struct ieee80211_local *local,
        ieee80211_txq_enqueue(local, txqi, skb);
        spin_unlock_bh(&fq->lock);
 
-       drv_wake_tx_queue(local, txqi);
+       schedule_and_wake_txq(local, txqi);
 
        return true;
 }
@@ -1938,9 +1945,16 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
                                int head_need, bool may_encrypt)
 {
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_hdr *hdr;
+       bool enc_tailroom;
        int tail_need = 0;
 
-       if (may_encrypt && sdata->crypto_tx_tailroom_needed_cnt) {
+       hdr = (struct ieee80211_hdr *) skb->data;
+       enc_tailroom = may_encrypt &&
+                      (sdata->crypto_tx_tailroom_needed_cnt ||
+                       ieee80211_is_mgmt(hdr->frame_control));
+
+       if (enc_tailroom) {
                tail_need = IEEE80211_ENCRYPT_TAILROOM;
                tail_need -= skb_tailroom(skb);
                tail_need = max_t(int, tail_need, 0);
@@ -1948,8 +1962,7 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
 
        if (skb_cloned(skb) &&
            (!ieee80211_hw_check(&local->hw, SUPPORTS_CLONED_SKBS) ||
-            !skb_clone_writable(skb, ETH_HLEN) ||
-            (may_encrypt && sdata->crypto_tx_tailroom_needed_cnt)))
+            !skb_clone_writable(skb, ETH_HLEN) || enc_tailroom))
                I802_DEBUG_INC(local->tx_expand_skb_head_cloned);
        else if (head_need || tail_need)
                I802_DEBUG_INC(local->tx_expand_skb_head);
@@ -3630,6 +3643,151 @@ out:
 }
 EXPORT_SYMBOL(ieee80211_tx_dequeue);
 
+struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct txq_info *txqi = NULL;
+
+       lockdep_assert_held(&local->active_txq_lock[ac]);
+
+ begin:
+       txqi = list_first_entry_or_null(&local->active_txqs[ac],
+                                       struct txq_info,
+                                       schedule_order);
+       if (!txqi)
+               return NULL;
+
+       if (txqi->txq.sta) {
+               struct sta_info *sta = container_of(txqi->txq.sta,
+                                               struct sta_info, sta);
+
+               if (sta->airtime[txqi->txq.ac].deficit < 0) {
+                       sta->airtime[txqi->txq.ac].deficit +=
+                               sta->airtime_weight;
+                       list_move_tail(&txqi->schedule_order,
+                                      &local->active_txqs[txqi->txq.ac]);
+                       goto begin;
+               }
+       }
+
+
+       if (txqi->schedule_round == local->schedule_round[ac])
+               return NULL;
+
+       list_del_init(&txqi->schedule_order);
+       txqi->schedule_round = local->schedule_round[ac];
+       return &txqi->txq;
+}
+EXPORT_SYMBOL(ieee80211_next_txq);
+
+void ieee80211_return_txq(struct ieee80211_hw *hw,
+                         struct ieee80211_txq *txq)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct txq_info *txqi = to_txq_info(txq);
+
+       lockdep_assert_held(&local->active_txq_lock[txq->ac]);
+
+       if (list_empty(&txqi->schedule_order) &&
+           (!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets)) {
+               /* If airtime accounting is active, always enqueue STAs at the
+                * head of the list to ensure that they only get moved to the
+                * back by the airtime DRR scheduler once they have a negative
+                * deficit. A station that already has a negative deficit will
+                * get immediately moved to the back of the list on the next
+                * call to ieee80211_next_txq().
+                */
+               if (txqi->txq.sta &&
+                   wiphy_ext_feature_isset(local->hw.wiphy,
+                                           NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+                       list_add(&txqi->schedule_order,
+                                &local->active_txqs[txq->ac]);
+               else
+                       list_add_tail(&txqi->schedule_order,
+                                     &local->active_txqs[txq->ac]);
+       }
+}
+EXPORT_SYMBOL(ieee80211_return_txq);
+
+void ieee80211_schedule_txq(struct ieee80211_hw *hw,
+                           struct ieee80211_txq *txq)
+       __acquires(txq_lock) __releases(txq_lock)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       spin_lock_bh(&local->active_txq_lock[txq->ac]);
+       ieee80211_return_txq(hw, txq);
+       spin_unlock_bh(&local->active_txq_lock[txq->ac]);
+}
+EXPORT_SYMBOL(ieee80211_schedule_txq);
+
+bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
+                               struct ieee80211_txq *txq)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct txq_info *iter, *tmp, *txqi = to_txq_info(txq);
+       struct sta_info *sta;
+       u8 ac = txq->ac;
+
+       lockdep_assert_held(&local->active_txq_lock[ac]);
+
+       if (!txqi->txq.sta)
+               goto out;
+
+       if (list_empty(&txqi->schedule_order))
+               goto out;
+
+       list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
+                                schedule_order) {
+               if (iter == txqi)
+                       break;
+
+               if (!iter->txq.sta) {
+                       list_move_tail(&iter->schedule_order,
+                                      &local->active_txqs[ac]);
+                       continue;
+               }
+               sta = container_of(iter->txq.sta, struct sta_info, sta);
+               if (sta->airtime[ac].deficit < 0)
+                       sta->airtime[ac].deficit += sta->airtime_weight;
+               list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
+       }
+
+       sta = container_of(txqi->txq.sta, struct sta_info, sta);
+       if (sta->airtime[ac].deficit >= 0)
+               goto out;
+
+       sta->airtime[ac].deficit += sta->airtime_weight;
+       list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
+
+       return false;
+out:
+       if (!list_empty(&txqi->schedule_order))
+               list_del_init(&txqi->schedule_order);
+
+       return true;
+}
+EXPORT_SYMBOL(ieee80211_txq_may_transmit);
+
+void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
+       __acquires(txq_lock)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       spin_lock_bh(&local->active_txq_lock[ac]);
+       local->schedule_round[ac]++;
+}
+EXPORT_SYMBOL(ieee80211_txq_schedule_start);
+
+void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
+       __releases(txq_lock)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       spin_unlock_bh(&local->active_txq_lock[ac]);
+}
+EXPORT_SYMBOL(ieee80211_txq_schedule_end);
+
 void __ieee80211_subif_start_xmit(struct sk_buff *skb,
                                  struct net_device *dev,
                                  u32 info_flags)
index d0eb38b..ba950ae 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright 2007      Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright (C) 2015-2017     Intel Deutschland GmbH
- * Copyright (C) 2018 Intel Corporation
+ * Copyright (C) 2018-2019 Intel Corporation
  *
  * 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
@@ -2146,6 +2146,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                case NL80211_IFTYPE_AP_VLAN:
                case NL80211_IFTYPE_MONITOR:
                        break;
+               case NL80211_IFTYPE_ADHOC:
+                       if (sdata->vif.bss_conf.ibss_joined)
+                               WARN_ON(drv_join_ibss(local, sdata));
+                       /* fall through */
                default:
                        ieee80211_reconfig_stations(sdata);
                        /* fall through */
index 94f53a9..dda8930 100644 (file)
@@ -183,8 +183,8 @@ static int mpls_build_state(struct nlattr *nla,
                           &n_labels, NULL, extack))
                return -EINVAL;
 
-       newts = lwtunnel_state_alloc(sizeof(*tun_encap_info) +
-                                    n_labels * sizeof(u32));
+       newts = lwtunnel_state_alloc(struct_size(tun_encap_info, label,
+                                                n_labels));
        if (!newts)
                return -ENOMEM;
 
index cad48d0..8401cef 100644 (file)
@@ -29,6 +29,7 @@ config        IP_VS_IPV6
        bool "IPv6 support for IPVS"
        depends on IPV6 = y || IP_VS = IPV6
        select IP6_NF_IPTABLES
+       select NF_DEFRAG_IPV6
        ---help---
          Add IPv6 support to IPVS.
 
index e969dad..43bbaa3 100644 (file)
@@ -1566,14 +1566,12 @@ ip_vs_try_to_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
                /* sorry, all this trouble for a no-hit :) */
                IP_VS_DBG_PKT(12, af, pp, skb, iph->off,
                              "ip_vs_in: packet continues traversal as normal");
-               if (iph->fragoffs) {
-                       /* Fragment that couldn't be mapped to a conn entry
-                        * is missing module nf_defrag_ipv6
-                        */
-                       IP_VS_DBG_RL("Unhandled frag, load nf_defrag_ipv6\n");
+
+               /* Fragment couldn't be mapped to a conn entry */
+               if (iph->fragoffs)
                        IP_VS_DBG_PKT(7, af, pp, skb, iph->off,
                                      "unhandled fragment");
-               }
+
                *verdict = NF_ACCEPT;
                return 0;
        }
index 446beeb..3c36480 100644 (file)
@@ -43,6 +43,7 @@
 #ifdef CONFIG_IP_VS_IPV6
 #include <net/ipv6.h>
 #include <net/ip6_route.h>
+#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
 #endif
 #include <net/route.h>
 #include <net/sock.h>
@@ -895,6 +896,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest,
 {
        struct ip_vs_dest *dest;
        unsigned int atype, i;
+       int ret = 0;
 
        EnterFunction(2);
 
@@ -905,6 +907,10 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest,
                        atype & IPV6_ADDR_LINKLOCAL) &&
                        !__ip_vs_addr_is_local_v6(svc->ipvs->net, &udest->addr.in6))
                        return -EINVAL;
+
+               ret = nf_defrag_ipv6_enable(svc->ipvs->net);
+               if (ret)
+                       return ret;
        } else
 #endif
        {
@@ -1228,6 +1234,10 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
                        ret = -EINVAL;
                        goto out_err;
                }
+
+               ret = nf_defrag_ipv6_enable(ipvs->net);
+               if (ret)
+                       goto out_err;
        }
 #endif
 
@@ -2221,6 +2231,18 @@ static int ip_vs_set_timeout(struct netns_ipvs *ipvs, struct ip_vs_timeout_user
                  u->udp_timeout);
 
 #ifdef CONFIG_IP_VS_PROTO_TCP
+       if (u->tcp_timeout < 0 || u->tcp_timeout > (INT_MAX / HZ) ||
+           u->tcp_fin_timeout < 0 || u->tcp_fin_timeout > (INT_MAX / HZ)) {
+               return -EINVAL;
+       }
+#endif
+
+#ifdef CONFIG_IP_VS_PROTO_UDP
+       if (u->udp_timeout < 0 || u->udp_timeout > (INT_MAX / HZ))
+               return -EINVAL;
+#endif
+
+#ifdef CONFIG_IP_VS_PROTO_TCP
        if (u->tcp_timeout) {
                pd = ip_vs_proto_data_get(ipvs, IPPROTO_TCP);
                pd->timeout_table[IP_VS_TCP_S_ESTABLISHED]
index 85de2a7..e139c25 100644 (file)
@@ -1050,6 +1050,22 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple,
                }
 
                if (nf_ct_key_equal(h, tuple, zone, net)) {
+                       /* Tuple is taken already, so caller will need to find
+                        * a new source port to use.
+                        *
+                        * Only exception:
+                        * If the *original tuples* are identical, then both
+                        * conntracks refer to the same flow.
+                        * This is a rare situation, it can occur e.g. when
+                        * more than one UDP packet is sent from same socket
+                        * in different threads.
+                        *
+                        * Let nf_ct_resolve_clash() deal with this later.
+                        */
+                       if (nf_ct_tuple_equal(&ignored_conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
+                                             &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple))
+                               continue;
+
                        NF_CT_STAT_INC_ATOMIC(net, found);
                        rcu_read_unlock();
                        return 1;
index 7495f29..de3f1e2 100644 (file)
@@ -131,6 +131,23 @@ static void nft_trans_destroy(struct nft_trans *trans)
        kfree(trans);
 }
 
+static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
+{
+       struct net *net = ctx->net;
+       struct nft_trans *trans;
+
+       if (!nft_set_is_anonymous(set))
+               return;
+
+       list_for_each_entry_reverse(trans, &net->nft.commit_list, list) {
+               if (trans->msg_type == NFT_MSG_NEWSET &&
+                   nft_trans_set(trans) == set) {
+                       nft_trans_set_bound(trans) = true;
+                       break;
+               }
+       }
+}
+
 static int nf_tables_register_hook(struct net *net,
                                   const struct nft_table *table,
                                   struct nft_chain *chain)
@@ -226,18 +243,6 @@ static int nft_delchain(struct nft_ctx *ctx)
        return err;
 }
 
-/* either expr ops provide both activate/deactivate, or neither */
-static bool nft_expr_check_ops(const struct nft_expr_ops *ops)
-{
-       if (!ops)
-               return true;
-
-       if (WARN_ON_ONCE((!ops->activate ^ !ops->deactivate)))
-               return false;
-
-       return true;
-}
-
 static void nft_rule_expr_activate(const struct nft_ctx *ctx,
                                   struct nft_rule *rule)
 {
@@ -253,14 +258,15 @@ static void nft_rule_expr_activate(const struct nft_ctx *ctx,
 }
 
 static void nft_rule_expr_deactivate(const struct nft_ctx *ctx,
-                                    struct nft_rule *rule)
+                                    struct nft_rule *rule,
+                                    enum nft_trans_phase phase)
 {
        struct nft_expr *expr;
 
        expr = nft_expr_first(rule);
        while (expr != nft_expr_last(rule) && expr->ops) {
                if (expr->ops->deactivate)
-                       expr->ops->deactivate(ctx, expr);
+                       expr->ops->deactivate(ctx, expr, phase);
 
                expr = nft_expr_next(expr);
        }
@@ -311,7 +317,7 @@ static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule)
                nft_trans_destroy(trans);
                return err;
        }
-       nft_rule_expr_deactivate(ctx, rule);
+       nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_PREPARE);
 
        return 0;
 }
@@ -1972,9 +1978,6 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk,
  */
 int nft_register_expr(struct nft_expr_type *type)
 {
-       if (!nft_expr_check_ops(type->ops))
-               return -EINVAL;
-
        nfnl_lock(NFNL_SUBSYS_NFTABLES);
        if (type->family == NFPROTO_UNSPEC)
                list_add_tail_rcu(&type->list, &nf_tables_expressions);
@@ -2122,10 +2125,6 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx,
                        err = PTR_ERR(ops);
                        goto err1;
                }
-               if (!nft_expr_check_ops(ops)) {
-                       err = -EINVAL;
-                       goto err1;
-               }
        } else
                ops = type->ops;
 
@@ -2555,7 +2554,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
 static void nf_tables_rule_release(const struct nft_ctx *ctx,
                                   struct nft_rule *rule)
 {
-       nft_rule_expr_deactivate(ctx, rule);
+       nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE);
        nf_tables_rule_destroy(ctx, rule);
 }
 
@@ -3761,39 +3760,30 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
 bind:
        binding->chain = ctx->chain;
        list_add_tail_rcu(&binding->list, &set->bindings);
+       nft_set_trans_bind(ctx, set);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(nf_tables_bind_set);
 
-void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set,
-                         struct nft_set_binding *binding)
-{
-       if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
-           nft_is_active(ctx->net, set))
-               list_add_tail_rcu(&set->list, &ctx->table->sets);
-
-       list_add_tail_rcu(&binding->list, &set->bindings);
-}
-EXPORT_SYMBOL_GPL(nf_tables_rebind_set);
-
 void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
-                         struct nft_set_binding *binding)
+                         struct nft_set_binding *binding, bool event)
 {
        list_del_rcu(&binding->list);
 
-       if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
-           nft_is_active(ctx->net, set))
+       if (list_empty(&set->bindings) && nft_set_is_anonymous(set)) {
                list_del_rcu(&set->list);
+               if (event)
+                       nf_tables_set_notify(ctx, set, NFT_MSG_DELSET,
+                                            GFP_KERNEL);
+       }
 }
 EXPORT_SYMBOL_GPL(nf_tables_unbind_set);
 
 void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set)
 {
-       if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
-           nft_is_active(ctx->net, set)) {
-               nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
+       if (list_empty(&set->bindings) && nft_set_is_anonymous(set))
                nft_set_destroy(set);
-       }
 }
 EXPORT_SYMBOL_GPL(nf_tables_destroy_set);
 
@@ -6622,6 +6612,9 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                        nf_tables_rule_notify(&trans->ctx,
                                              nft_trans_rule(trans),
                                              NFT_MSG_DELRULE);
+                       nft_rule_expr_deactivate(&trans->ctx,
+                                                nft_trans_rule(trans),
+                                                NFT_TRANS_COMMIT);
                        break;
                case NFT_MSG_NEWSET:
                        nft_clear(net, nft_trans_set(trans));
@@ -6708,7 +6701,8 @@ static void nf_tables_abort_release(struct nft_trans *trans)
                nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
                break;
        case NFT_MSG_NEWSET:
-               nft_set_destroy(nft_trans_set(trans));
+               if (!nft_trans_set_bound(trans))
+                       nft_set_destroy(nft_trans_set(trans));
                break;
        case NFT_MSG_NEWSETELEM:
                nft_set_elem_destroy(nft_trans_elem_set(trans),
@@ -6769,7 +6763,9 @@ static int __nf_tables_abort(struct net *net)
                case NFT_MSG_NEWRULE:
                        trans->ctx.chain->use--;
                        list_del_rcu(&nft_trans_rule(trans)->list);
-                       nft_rule_expr_deactivate(&trans->ctx, nft_trans_rule(trans));
+                       nft_rule_expr_deactivate(&trans->ctx,
+                                                nft_trans_rule(trans),
+                                                NFT_TRANS_ABORT);
                        break;
                case NFT_MSG_DELRULE:
                        trans->ctx.chain->use++;
@@ -6779,7 +6775,8 @@ static int __nf_tables_abort(struct net *net)
                        break;
                case NFT_MSG_NEWSET:
                        trans->ctx.table->use--;
-                       list_del_rcu(&nft_trans_set(trans)->list);
+                       if (!nft_trans_set_bound(trans))
+                               list_del_rcu(&nft_trans_set(trans)->list);
                        break;
                case NFT_MSG_DELSET:
                        trans->ctx.table->use++;
index 6f41dd7..1f1d90c 100644 (file)
@@ -66,6 +66,7 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
                             int ttl_check,
                             struct nf_osf_hdr_ctx *ctx)
 {
+       const __u8 *optpinit = ctx->optp;
        unsigned int check_WSS = 0;
        int fmatch = FMATCH_WRONG;
        int foptsize, optnum;
@@ -155,6 +156,9 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
                }
        }
 
+       if (fmatch != FMATCH_OK)
+               ctx->optp = optpinit;
+
        return fmatch == FMATCH_OK;
 }
 
index 7334e0b..0a4bad5 100644 (file)
 #include <linux/netfilter_bridge/ebtables.h>
 #include <linux/netfilter_arp/arp_tables.h>
 #include <net/netfilter/nf_tables.h>
+#include <net/netns/generic.h>
 
 struct nft_xt {
        struct list_head        head;
        struct nft_expr_ops     ops;
-       unsigned int            refcnt;
+       refcount_t              refcnt;
+
+       /* used only when transaction mutex is locked */
+       unsigned int            listcnt;
 
        /* Unlike other expressions, ops doesn't have static storage duration.
         * nft core assumes they do.  We use kfree_rcu so that nft core can
@@ -43,10 +47,39 @@ struct nft_xt_match_priv {
        void *info;
 };
 
+struct nft_compat_net {
+       struct list_head nft_target_list;
+       struct list_head nft_match_list;
+};
+
+static unsigned int nft_compat_net_id __read_mostly;
+static struct nft_expr_type nft_match_type;
+static struct nft_expr_type nft_target_type;
+
+static struct nft_compat_net *nft_compat_pernet(struct net *net)
+{
+       return net_generic(net, nft_compat_net_id);
+}
+
+static void nft_xt_get(struct nft_xt *xt)
+{
+       /* refcount_inc() warns on 0 -> 1 transition, but we can't
+        * init the reference count to 1 in .select_ops -- we can't
+        * undo such an increase when another expression inside the same
+        * rule fails afterwards.
+        */
+       if (xt->listcnt == 0)
+               refcount_set(&xt->refcnt, 1);
+       else
+               refcount_inc(&xt->refcnt);
+
+       xt->listcnt++;
+}
+
 static bool nft_xt_put(struct nft_xt *xt)
 {
-       if (--xt->refcnt == 0) {
-               list_del(&xt->head);
+       if (refcount_dec_and_test(&xt->refcnt)) {
+               WARN_ON_ONCE(!list_empty(&xt->head));
                kfree_rcu(xt, rcu_head);
                return true;
        }
@@ -273,7 +306,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
                return -EINVAL;
 
        nft_xt = container_of(expr->ops, struct nft_xt, ops);
-       nft_xt->refcnt++;
+       nft_xt_get(nft_xt);
        return 0;
 }
 
@@ -282,6 +315,7 @@ nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 {
        struct xt_target *target = expr->ops->data;
        void *info = nft_expr_priv(expr);
+       struct module *me = target->me;
        struct xt_tgdtor_param par;
 
        par.net = ctx->net;
@@ -292,7 +326,7 @@ nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
                par.target->destroy(&par);
 
        if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops)))
-               module_put(target->me);
+               module_put(me);
 }
 
 static int nft_extension_dump_info(struct sk_buff *skb, int attr,
@@ -486,7 +520,7 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
                return ret;
 
        nft_xt = container_of(expr->ops, struct nft_xt, ops);
-       nft_xt->refcnt++;
+       nft_xt_get(nft_xt);
        return 0;
 }
 
@@ -540,6 +574,18 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
        __nft_match_destroy(ctx, expr, nft_expr_priv(expr));
 }
 
+static void nft_compat_deactivate(const struct nft_ctx *ctx,
+                                 const struct nft_expr *expr,
+                                 enum nft_trans_phase phase)
+{
+       struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
+
+       if (phase == NFT_TRANS_ABORT || phase == NFT_TRANS_COMMIT) {
+               if (--xt->listcnt == 0)
+                       list_del_init(&xt->head);
+       }
+}
+
 static void
 nft_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 {
@@ -734,10 +780,6 @@ static const struct nfnetlink_subsystem nfnl_compat_subsys = {
        .cb             = nfnl_nft_compat_cb,
 };
 
-static LIST_HEAD(nft_match_list);
-
-static struct nft_expr_type nft_match_type;
-
 static bool nft_match_cmp(const struct xt_match *match,
                          const char *name, u32 rev, u32 family)
 {
@@ -749,6 +791,7 @@ static const struct nft_expr_ops *
 nft_match_select_ops(const struct nft_ctx *ctx,
                     const struct nlattr * const tb[])
 {
+       struct nft_compat_net *cn;
        struct nft_xt *nft_match;
        struct xt_match *match;
        unsigned int matchsize;
@@ -765,8 +808,10 @@ nft_match_select_ops(const struct nft_ctx *ctx,
        rev = ntohl(nla_get_be32(tb[NFTA_MATCH_REV]));
        family = ctx->family;
 
+       cn = nft_compat_pernet(ctx->net);
+
        /* Re-use the existing match if it's already loaded. */
-       list_for_each_entry(nft_match, &nft_match_list, head) {
+       list_for_each_entry(nft_match, &cn->nft_match_list, head) {
                struct xt_match *match = nft_match->ops.data;
 
                if (nft_match_cmp(match, mt_name, rev, family))
@@ -789,11 +834,12 @@ nft_match_select_ops(const struct nft_ctx *ctx,
                goto err;
        }
 
-       nft_match->refcnt = 0;
+       refcount_set(&nft_match->refcnt, 0);
        nft_match->ops.type = &nft_match_type;
        nft_match->ops.eval = nft_match_eval;
        nft_match->ops.init = nft_match_init;
        nft_match->ops.destroy = nft_match_destroy;
+       nft_match->ops.deactivate = nft_compat_deactivate;
        nft_match->ops.dump = nft_match_dump;
        nft_match->ops.validate = nft_match_validate;
        nft_match->ops.data = match;
@@ -810,7 +856,8 @@ nft_match_select_ops(const struct nft_ctx *ctx,
 
        nft_match->ops.size = matchsize;
 
-       list_add(&nft_match->head, &nft_match_list);
+       nft_match->listcnt = 0;
+       list_add(&nft_match->head, &cn->nft_match_list);
 
        return &nft_match->ops;
 err:
@@ -826,10 +873,6 @@ static struct nft_expr_type nft_match_type __read_mostly = {
        .owner          = THIS_MODULE,
 };
 
-static LIST_HEAD(nft_target_list);
-
-static struct nft_expr_type nft_target_type;
-
 static bool nft_target_cmp(const struct xt_target *tg,
                           const char *name, u32 rev, u32 family)
 {
@@ -841,6 +884,7 @@ static const struct nft_expr_ops *
 nft_target_select_ops(const struct nft_ctx *ctx,
                      const struct nlattr * const tb[])
 {
+       struct nft_compat_net *cn;
        struct nft_xt *nft_target;
        struct xt_target *target;
        char *tg_name;
@@ -861,8 +905,9 @@ nft_target_select_ops(const struct nft_ctx *ctx,
            strcmp(tg_name, "standard") == 0)
                return ERR_PTR(-EINVAL);
 
+       cn = nft_compat_pernet(ctx->net);
        /* Re-use the existing target if it's already loaded. */
-       list_for_each_entry(nft_target, &nft_target_list, head) {
+       list_for_each_entry(nft_target, &cn->nft_target_list, head) {
                struct xt_target *target = nft_target->ops.data;
 
                if (!target->target)
@@ -893,11 +938,12 @@ nft_target_select_ops(const struct nft_ctx *ctx,
                goto err;
        }
 
-       nft_target->refcnt = 0;
+       refcount_set(&nft_target->refcnt, 0);
        nft_target->ops.type = &nft_target_type;
        nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
        nft_target->ops.init = nft_target_init;
        nft_target->ops.destroy = nft_target_destroy;
+       nft_target->ops.deactivate = nft_compat_deactivate;
        nft_target->ops.dump = nft_target_dump;
        nft_target->ops.validate = nft_target_validate;
        nft_target->ops.data = target;
@@ -907,7 +953,8 @@ nft_target_select_ops(const struct nft_ctx *ctx,
        else
                nft_target->ops.eval = nft_target_eval_xt;
 
-       list_add(&nft_target->head, &nft_target_list);
+       nft_target->listcnt = 0;
+       list_add(&nft_target->head, &cn->nft_target_list);
 
        return &nft_target->ops;
 err:
@@ -923,13 +970,74 @@ static struct nft_expr_type nft_target_type __read_mostly = {
        .owner          = THIS_MODULE,
 };
 
+static int __net_init nft_compat_init_net(struct net *net)
+{
+       struct nft_compat_net *cn = nft_compat_pernet(net);
+
+       INIT_LIST_HEAD(&cn->nft_target_list);
+       INIT_LIST_HEAD(&cn->nft_match_list);
+
+       return 0;
+}
+
+static void __net_exit nft_compat_exit_net(struct net *net)
+{
+       struct nft_compat_net *cn = nft_compat_pernet(net);
+       struct nft_xt *xt, *next;
+
+       if (list_empty(&cn->nft_match_list) &&
+           list_empty(&cn->nft_target_list))
+               return;
+
+       /* If there was an error that caused nft_xt expr to not be initialized
+        * fully and noone else requested the same expression later, the lists
+        * contain 0-refcount entries that still hold module reference.
+        *
+        * Clean them here.
+        */
+       mutex_lock(&net->nft.commit_mutex);
+       list_for_each_entry_safe(xt, next, &cn->nft_target_list, head) {
+               struct xt_target *target = xt->ops.data;
+
+               list_del_init(&xt->head);
+
+               if (refcount_read(&xt->refcnt))
+                       continue;
+               module_put(target->me);
+               kfree(xt);
+       }
+
+       list_for_each_entry_safe(xt, next, &cn->nft_match_list, head) {
+               struct xt_match *match = xt->ops.data;
+
+               list_del_init(&xt->head);
+
+               if (refcount_read(&xt->refcnt))
+                       continue;
+               module_put(match->me);
+               kfree(xt);
+       }
+       mutex_unlock(&net->nft.commit_mutex);
+}
+
+static struct pernet_operations nft_compat_net_ops = {
+       .init   = nft_compat_init_net,
+       .exit   = nft_compat_exit_net,
+       .id     = &nft_compat_net_id,
+       .size   = sizeof(struct nft_compat_net),
+};
+
 static int __init nft_compat_module_init(void)
 {
        int ret;
 
+       ret = register_pernet_subsys(&nft_compat_net_ops);
+       if (ret < 0)
+               goto err_target;
+
        ret = nft_register_expr(&nft_match_type);
        if (ret < 0)
-               return ret;
+               goto err_pernet;
 
        ret = nft_register_expr(&nft_target_type);
        if (ret < 0)
@@ -942,45 +1050,21 @@ static int __init nft_compat_module_init(void)
        }
 
        return ret;
-
 err_target:
        nft_unregister_expr(&nft_target_type);
 err_match:
        nft_unregister_expr(&nft_match_type);
+err_pernet:
+       unregister_pernet_subsys(&nft_compat_net_ops);
        return ret;
 }
 
 static void __exit nft_compat_module_exit(void)
 {
-       struct nft_xt *xt, *next;
-
-       /* list should be empty here, it can be non-empty only in case there
-        * was an error that caused nft_xt expr to not be initialized fully
-        * and noone else requested the same expression later.
-        *
-        * In this case, the lists contain 0-refcount entries that still
-        * hold module reference.
-        */
-       list_for_each_entry_safe(xt, next, &nft_target_list, head) {
-               struct xt_target *target = xt->ops.data;
-
-               if (WARN_ON_ONCE(xt->refcnt))
-                       continue;
-               module_put(target->me);
-               kfree(xt);
-       }
-
-       list_for_each_entry_safe(xt, next, &nft_match_list, head) {
-               struct xt_match *match = xt->ops.data;
-
-               if (WARN_ON_ONCE(xt->refcnt))
-                       continue;
-               module_put(match->me);
-               kfree(xt);
-       }
        nfnetlink_subsys_unregister(&nfnl_compat_subsys);
        nft_unregister_expr(&nft_target_type);
        nft_unregister_expr(&nft_match_type);
+       unregister_pernet_subsys(&nft_compat_net_ops);
 }
 
 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT);
index 9658493..a8a74a1 100644 (file)
@@ -234,20 +234,17 @@ err1:
        return err;
 }
 
-static void nft_dynset_activate(const struct nft_ctx *ctx,
-                               const struct nft_expr *expr)
-{
-       struct nft_dynset *priv = nft_expr_priv(expr);
-
-       nf_tables_rebind_set(ctx, priv->set, &priv->binding);
-}
-
 static void nft_dynset_deactivate(const struct nft_ctx *ctx,
-                                 const struct nft_expr *expr)
+                                 const struct nft_expr *expr,
+                                 enum nft_trans_phase phase)
 {
        struct nft_dynset *priv = nft_expr_priv(expr);
 
-       nf_tables_unbind_set(ctx, priv->set, &priv->binding);
+       if (phase == NFT_TRANS_PREPARE)
+               return;
+
+       nf_tables_unbind_set(ctx, priv->set, &priv->binding,
+                            phase == NFT_TRANS_COMMIT);
 }
 
 static void nft_dynset_destroy(const struct nft_ctx *ctx,
@@ -295,7 +292,6 @@ static const struct nft_expr_ops nft_dynset_ops = {
        .eval           = nft_dynset_eval,
        .init           = nft_dynset_init,
        .destroy        = nft_dynset_destroy,
-       .activate       = nft_dynset_activate,
        .deactivate     = nft_dynset_deactivate,
        .dump           = nft_dynset_dump,
 };
index 3e5ed78..5ec4312 100644 (file)
@@ -72,10 +72,14 @@ static void nft_immediate_activate(const struct nft_ctx *ctx,
 }
 
 static void nft_immediate_deactivate(const struct nft_ctx *ctx,
-                                    const struct nft_expr *expr)
+                                    const struct nft_expr *expr,
+                                    enum nft_trans_phase phase)
 {
        const struct nft_immediate_expr *priv = nft_expr_priv(expr);
 
+       if (phase == NFT_TRANS_COMMIT)
+               return;
+
        return nft_data_release(&priv->data, nft_dreg_to_type(priv->dreg));
 }
 
index 227b2b1..14496da 100644 (file)
@@ -121,20 +121,17 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
        return 0;
 }
 
-static void nft_lookup_activate(const struct nft_ctx *ctx,
-                               const struct nft_expr *expr)
-{
-       struct nft_lookup *priv = nft_expr_priv(expr);
-
-       nf_tables_rebind_set(ctx, priv->set, &priv->binding);
-}
-
 static void nft_lookup_deactivate(const struct nft_ctx *ctx,
-                                 const struct nft_expr *expr)
+                                 const struct nft_expr *expr,
+                                 enum nft_trans_phase phase)
 {
        struct nft_lookup *priv = nft_expr_priv(expr);
 
-       nf_tables_unbind_set(ctx, priv->set, &priv->binding);
+       if (phase == NFT_TRANS_PREPARE)
+               return;
+
+       nf_tables_unbind_set(ctx, priv->set, &priv->binding,
+                            phase == NFT_TRANS_COMMIT);
 }
 
 static void nft_lookup_destroy(const struct nft_ctx *ctx,
@@ -225,7 +222,6 @@ static const struct nft_expr_ops nft_lookup_ops = {
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
        .eval           = nft_lookup_eval,
        .init           = nft_lookup_init,
-       .activate       = nft_lookup_activate,
        .deactivate     = nft_lookup_deactivate,
        .destroy        = nft_lookup_destroy,
        .dump           = nft_lookup_dump,
index c1f2adf..79ef074 100644 (file)
@@ -156,20 +156,17 @@ nla_put_failure:
        return -1;
 }
 
-static void nft_objref_map_activate(const struct nft_ctx *ctx,
-                                   const struct nft_expr *expr)
-{
-       struct nft_objref_map *priv = nft_expr_priv(expr);
-
-       nf_tables_rebind_set(ctx, priv->set, &priv->binding);
-}
-
 static void nft_objref_map_deactivate(const struct nft_ctx *ctx,
-                                     const struct nft_expr *expr)
+                                     const struct nft_expr *expr,
+                                     enum nft_trans_phase phase)
 {
        struct nft_objref_map *priv = nft_expr_priv(expr);
 
-       nf_tables_unbind_set(ctx, priv->set, &priv->binding);
+       if (phase == NFT_TRANS_PREPARE)
+               return;
+
+       nf_tables_unbind_set(ctx, priv->set, &priv->binding,
+                            phase == NFT_TRANS_COMMIT);
 }
 
 static void nft_objref_map_destroy(const struct nft_ctx *ctx,
@@ -186,7 +183,6 @@ static const struct nft_expr_ops nft_objref_map_ops = {
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)),
        .eval           = nft_objref_map_eval,
        .init           = nft_objref_map_init,
-       .activate       = nft_objref_map_activate,
        .deactivate     = nft_objref_map_deactivate,
        .destroy        = nft_objref_map_destroy,
        .dump           = nft_objref_map_dump,
index aecadd4..13e1ac3 100644 (file)
@@ -1899,7 +1899,7 @@ static int __init xt_init(void)
                seqcount_init(&per_cpu(xt_recseq, i));
        }
 
-       xt = kmalloc_array(NFPROTO_NUMPROTO, sizeof(struct xt_af), GFP_KERNEL);
+       xt = kcalloc(NFPROTO_NUMPROTO, sizeof(struct xt_af), GFP_KERNEL);
        if (!xt)
                return -ENOMEM;
 
index cbd51ed..908e53a 100644 (file)
@@ -52,21 +52,21 @@ void nr_start_t1timer(struct sock *sk)
 {
        struct nr_sock *nr = nr_sk(sk);
 
-       mod_timer(&nr->t1timer, jiffies + nr->t1);
+       sk_reset_timer(sk, &nr->t1timer, jiffies + nr->t1);
 }
 
 void nr_start_t2timer(struct sock *sk)
 {
        struct nr_sock *nr = nr_sk(sk);
 
-       mod_timer(&nr->t2timer, jiffies + nr->t2);
+       sk_reset_timer(sk, &nr->t2timer, jiffies + nr->t2);
 }
 
 void nr_start_t4timer(struct sock *sk)
 {
        struct nr_sock *nr = nr_sk(sk);
 
-       mod_timer(&nr->t4timer, jiffies + nr->t4);
+       sk_reset_timer(sk, &nr->t4timer, jiffies + nr->t4);
 }
 
 void nr_start_idletimer(struct sock *sk)
@@ -74,37 +74,37 @@ void nr_start_idletimer(struct sock *sk)
        struct nr_sock *nr = nr_sk(sk);
 
        if (nr->idle > 0)
-               mod_timer(&nr->idletimer, jiffies + nr->idle);
+               sk_reset_timer(sk, &nr->idletimer, jiffies + nr->idle);
 }
 
 void nr_start_heartbeat(struct sock *sk)
 {
-       mod_timer(&sk->sk_timer, jiffies + 5 * HZ);
+       sk_reset_timer(sk, &sk->sk_timer, jiffies + 5 * HZ);
 }
 
 void nr_stop_t1timer(struct sock *sk)
 {
-       del_timer(&nr_sk(sk)->t1timer);
+       sk_stop_timer(sk, &nr_sk(sk)->t1timer);
 }
 
 void nr_stop_t2timer(struct sock *sk)
 {
-       del_timer(&nr_sk(sk)->t2timer);
+       sk_stop_timer(sk, &nr_sk(sk)->t2timer);
 }
 
 void nr_stop_t4timer(struct sock *sk)
 {
-       del_timer(&nr_sk(sk)->t4timer);
+       sk_stop_timer(sk, &nr_sk(sk)->t4timer);
 }
 
 void nr_stop_idletimer(struct sock *sk)
 {
-       del_timer(&nr_sk(sk)->idletimer);
+       sk_stop_timer(sk, &nr_sk(sk)->idletimer);
 }
 
 void nr_stop_heartbeat(struct sock *sk)
 {
-       del_timer(&sk->sk_timer);
+       sk_stop_timer(sk, &sk->sk_timer);
 }
 
 int nr_t1timer_running(struct sock *sk)
index 3b1a789..1cd1d83 100644 (file)
@@ -4292,7 +4292,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
                rb->frames_per_block = req->tp_block_size / req->tp_frame_size;
                if (unlikely(rb->frames_per_block == 0))
                        goto out;
-               if (unlikely(req->tp_block_size > UINT_MAX / req->tp_block_nr))
+               if (unlikely(rb->frames_per_block > UINT_MAX / req->tp_block_nr))
                        goto out;
                if (unlikely((rb->frames_per_block * req->tp_block_nr) !=
                                        req->tp_frame_nr))
index 65387e1..d6cc97f 100644 (file)
@@ -254,7 +254,40 @@ static __poll_t rds_poll(struct file *file, struct socket *sock,
 
 static int rds_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-       return -ENOIOCTLCMD;
+       struct rds_sock *rs = rds_sk_to_rs(sock->sk);
+       rds_tos_t utos, tos = 0;
+
+       switch (cmd) {
+       case SIOCRDSSETTOS:
+               if (get_user(utos, (rds_tos_t __user *)arg))
+                       return -EFAULT;
+
+               if (rs->rs_transport &&
+                   rs->rs_transport->get_tos_map)
+                       tos = rs->rs_transport->get_tos_map(utos);
+               else
+                       return -ENOIOCTLCMD;
+
+               spin_lock_bh(&rds_sock_lock);
+               if (rs->rs_tos || rs->rs_conn) {
+                       spin_unlock_bh(&rds_sock_lock);
+                       return -EINVAL;
+               }
+               rs->rs_tos = tos;
+               spin_unlock_bh(&rds_sock_lock);
+               break;
+       case SIOCRDSGETTOS:
+               spin_lock_bh(&rds_sock_lock);
+               tos = rs->rs_tos;
+               spin_unlock_bh(&rds_sock_lock);
+               if (put_user(tos, (rds_tos_t __user *)arg))
+                       return -EFAULT;
+               break;
+       default:
+               return -ENOIOCTLCMD;
+       }
+
+       return 0;
 }
 
 static int rds_cancel_sent_to(struct rds_sock *rs, char __user *optval,
@@ -348,7 +381,7 @@ static int rds_set_transport(struct rds_sock *rs, char __user *optval,
 }
 
 static int rds_enable_recvtstamp(struct sock *sk, char __user *optval,
-                                int optlen)
+                                int optlen, int optname)
 {
        int val, valbool;
 
@@ -360,6 +393,9 @@ static int rds_enable_recvtstamp(struct sock *sk, char __user *optval,
 
        valbool = val ? 1 : 0;
 
+       if (optname == SO_TIMESTAMP_NEW)
+               sock_set_flag(sk, SOCK_TSTAMP_NEW);
+
        if (valbool)
                sock_set_flag(sk, SOCK_RCVTSTAMP);
        else
@@ -430,9 +466,10 @@ static int rds_setsockopt(struct socket *sock, int level, int optname,
                ret = rds_set_transport(rs, optval, optlen);
                release_sock(sock->sk);
                break;
-       case SO_TIMESTAMP:
+       case SO_TIMESTAMP_OLD:
+       case SO_TIMESTAMP_NEW:
                lock_sock(sock->sk);
-               ret = rds_enable_recvtstamp(sock->sk, optval, optlen);
+               ret = rds_enable_recvtstamp(sock->sk, optval, optlen, optname);
                release_sock(sock->sk);
                break;
        case SO_RDS_MSG_RXPATH_LATENCY:
@@ -646,6 +683,8 @@ static int __rds_create(struct socket *sock, struct sock *sk, int protocol)
        spin_lock_init(&rs->rs_rdma_lock);
        rs->rs_rdma_keys = RB_ROOT;
        rs->rs_rx_traces = 0;
+       rs->rs_tos = 0;
+       rs->rs_conn = NULL;
 
        spin_lock_bh(&rds_sock_lock);
        list_add_tail(&rs->rs_item, &rds_sock_list);
index 762d2c6..17c9d9f 100644 (file)
@@ -78,10 +78,10 @@ struct rds_sock *rds_find_bound(const struct in6_addr *addr, __be16 port,
        __rds_create_bind_key(key, addr, port, scope_id);
        rcu_read_lock();
        rs = rhashtable_lookup(&bind_hash_table, key, ht_parms);
-       if (rs && !sock_flag(rds_rs_to_sk(rs), SOCK_DEAD))
-               rds_sock_addref(rs);
-       else
+       if (rs && (sock_flag(rds_rs_to_sk(rs), SOCK_DEAD) ||
+                  !refcount_inc_not_zero(&rds_rs_to_sk(rs)->sk_refcnt)))
                rs = NULL;
+
        rcu_read_unlock();
 
        rdsdebug("returning rs %p for %pI6c:%u\n", rs, addr,
index 3bd2f4a..7ea134f 100644 (file)
@@ -84,7 +84,7 @@ static struct rds_connection *rds_conn_lookup(struct net *net,
                                              const struct in6_addr *laddr,
                                              const struct in6_addr *faddr,
                                              struct rds_transport *trans,
-                                             int dev_if)
+                                             u8 tos, int dev_if)
 {
        struct rds_connection *conn, *ret = NULL;
 
@@ -92,6 +92,7 @@ static struct rds_connection *rds_conn_lookup(struct net *net,
                if (ipv6_addr_equal(&conn->c_faddr, faddr) &&
                    ipv6_addr_equal(&conn->c_laddr, laddr) &&
                    conn->c_trans == trans &&
+                   conn->c_tos == tos &&
                    net == rds_conn_net(conn) &&
                    conn->c_dev_if == dev_if) {
                        ret = conn;
@@ -139,6 +140,7 @@ static void __rds_conn_path_init(struct rds_connection *conn,
        atomic_set(&cp->cp_state, RDS_CONN_DOWN);
        cp->cp_send_gen = 0;
        cp->cp_reconnect_jiffies = 0;
+       cp->cp_conn->c_proposed_version = RDS_PROTOCOL_VERSION;
        INIT_DELAYED_WORK(&cp->cp_send_w, rds_send_worker);
        INIT_DELAYED_WORK(&cp->cp_recv_w, rds_recv_worker);
        INIT_DELAYED_WORK(&cp->cp_conn_w, rds_connect_worker);
@@ -159,7 +161,7 @@ static struct rds_connection *__rds_conn_create(struct net *net,
                                                const struct in6_addr *laddr,
                                                const struct in6_addr *faddr,
                                                struct rds_transport *trans,
-                                               gfp_t gfp,
+                                               gfp_t gfp, u8 tos,
                                                int is_outgoing,
                                                int dev_if)
 {
@@ -171,7 +173,7 @@ static struct rds_connection *__rds_conn_create(struct net *net,
        int npaths = (trans->t_mp_capable ? RDS_MPATH_WORKERS : 1);
 
        rcu_read_lock();
-       conn = rds_conn_lookup(net, head, laddr, faddr, trans, dev_if);
+       conn = rds_conn_lookup(net, head, laddr, faddr, trans, tos, dev_if);
        if (conn &&
            conn->c_loopback &&
            conn->c_trans != &rds_loop_transport &&
@@ -205,6 +207,7 @@ static struct rds_connection *__rds_conn_create(struct net *net,
        conn->c_isv6 = !ipv6_addr_v4mapped(laddr);
        conn->c_faddr = *faddr;
        conn->c_dev_if = dev_if;
+       conn->c_tos = tos;
 
 #if IS_ENABLED(CONFIG_IPV6)
        /* If the local address is link local, set c_bound_if to be the
@@ -297,7 +300,7 @@ static struct rds_connection *__rds_conn_create(struct net *net,
                struct rds_connection *found;
 
                found = rds_conn_lookup(net, head, laddr, faddr, trans,
-                                       dev_if);
+                                       tos, dev_if);
                if (found) {
                        struct rds_conn_path *cp;
                        int i;
@@ -332,10 +335,10 @@ out:
 struct rds_connection *rds_conn_create(struct net *net,
                                       const struct in6_addr *laddr,
                                       const struct in6_addr *faddr,
-                                      struct rds_transport *trans, gfp_t gfp,
-                                      int dev_if)
+                                      struct rds_transport *trans, u8 tos,
+                                      gfp_t gfp, int dev_if)
 {
-       return __rds_conn_create(net, laddr, faddr, trans, gfp, 0, dev_if);
+       return __rds_conn_create(net, laddr, faddr, trans, gfp, tos, 0, dev_if);
 }
 EXPORT_SYMBOL_GPL(rds_conn_create);
 
@@ -343,9 +346,9 @@ struct rds_connection *rds_conn_create_outgoing(struct net *net,
                                                const struct in6_addr *laddr,
                                                const struct in6_addr *faddr,
                                                struct rds_transport *trans,
-                                               gfp_t gfp, int dev_if)
+                                               u8 tos, gfp_t gfp, int dev_if)
 {
-       return __rds_conn_create(net, laddr, faddr, trans, gfp, 1, dev_if);
+       return __rds_conn_create(net, laddr, faddr, trans, gfp, tos, 1, dev_if);
 }
 EXPORT_SYMBOL_GPL(rds_conn_create_outgoing);
 
index 9d7b758..2da9b75 100644 (file)
@@ -301,6 +301,7 @@ static int rds_ib_conn_info_visitor(struct rds_connection *conn,
 
        iinfo->src_addr = conn->c_laddr.s6_addr32[3];
        iinfo->dst_addr = conn->c_faddr.s6_addr32[3];
+       iinfo->tos = conn->c_tos;
 
        memset(&iinfo->src_gid, 0, sizeof(iinfo->src_gid));
        memset(&iinfo->dst_gid, 0, sizeof(iinfo->dst_gid));
@@ -514,6 +515,15 @@ void rds_ib_exit(void)
        rds_ib_mr_exit();
 }
 
+static u8 rds_ib_get_tos_map(u8 tos)
+{
+       /* 1:1 user to transport map for RDMA transport.
+        * In future, if custom map is desired, hook can export
+        * user configurable map.
+        */
+       return tos;
+}
+
 struct rds_transport rds_ib_transport = {
        .laddr_check            = rds_ib_laddr_check,
        .xmit_path_complete     = rds_ib_xmit_path_complete,
@@ -536,6 +546,7 @@ struct rds_transport rds_ib_transport = {
        .sync_mr                = rds_ib_sync_mr,
        .free_mr                = rds_ib_free_mr,
        .flush_mrs              = rds_ib_flush_mrs,
+       .get_tos_map            = rds_ib_get_tos_map,
        .t_owner                = THIS_MODULE,
        .t_name                 = "infiniband",
        .t_unloading            = rds_ib_is_unloading,
index 71ff356..752f922 100644 (file)
@@ -67,7 +67,9 @@ struct rds_ib_conn_priv_cmn {
        u8                      ricpc_protocol_major;
        u8                      ricpc_protocol_minor;
        __be16                  ricpc_protocol_minor_mask;      /* bitmask */
-       __be32                  ricpc_reserved1;
+       u8                      ricpc_dp_toss;
+       u8                      ripc_reserved1;
+       __be16                  ripc_reserved2;
        __be64                  ricpc_ack_seq;
        __be32                  ricpc_credit;   /* non-zero enables flow ctl */
 };
index bfbb31f..66c6eb5 100644 (file)
@@ -133,23 +133,24 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
                rds_ib_set_flow_control(conn, be32_to_cpu(credit));
        }
 
-       if (conn->c_version < RDS_PROTOCOL(3, 1)) {
-               pr_notice("RDS/IB: Connection <%pI6c,%pI6c> version %u.%u no longer supported\n",
-                         &conn->c_laddr, &conn->c_faddr,
-                         RDS_PROTOCOL_MAJOR(conn->c_version),
-                         RDS_PROTOCOL_MINOR(conn->c_version));
-               set_bit(RDS_DESTROY_PENDING, &conn->c_path[0].cp_flags);
-               rds_conn_destroy(conn);
-               return;
-       } else {
-               pr_notice("RDS/IB: %s conn connected <%pI6c,%pI6c> version %u.%u%s\n",
-                         ic->i_active_side ? "Active" : "Passive",
-                         &conn->c_laddr, &conn->c_faddr,
-                         RDS_PROTOCOL_MAJOR(conn->c_version),
-                         RDS_PROTOCOL_MINOR(conn->c_version),
-                         ic->i_flowctl ? ", flow control" : "");
+       if (conn->c_version < RDS_PROTOCOL_VERSION) {
+               if (conn->c_version != RDS_PROTOCOL_COMPAT_VERSION) {
+                       pr_notice("RDS/IB: Connection <%pI6c,%pI6c> version %u.%u no longer supported\n",
+                                 &conn->c_laddr, &conn->c_faddr,
+                                 RDS_PROTOCOL_MAJOR(conn->c_version),
+                                 RDS_PROTOCOL_MINOR(conn->c_version));
+                       rds_conn_destroy(conn);
+                       return;
+               }
        }
 
+       pr_notice("RDS/IB: %s conn connected <%pI6c,%pI6c,%d> version %u.%u%s\n",
+                 ic->i_active_side ? "Active" : "Passive",
+                 &conn->c_laddr, &conn->c_faddr, conn->c_tos,
+                 RDS_PROTOCOL_MAJOR(conn->c_version),
+                 RDS_PROTOCOL_MINOR(conn->c_version),
+                 ic->i_flowctl ? ", flow control" : "");
+
        atomic_set(&ic->i_cq_quiesce, 0);
 
        /* Init rings and fill recv. this needs to wait until protocol
@@ -184,6 +185,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
                                            NULL);
        }
 
+       conn->c_proposed_version = conn->c_version;
        rds_connect_complete(conn);
 }
 
@@ -220,6 +222,7 @@ static void rds_ib_cm_fill_conn_param(struct rds_connection *conn,
                            cpu_to_be16(RDS_IB_SUPPORTED_PROTOCOLS);
                        dp->ricp_v6.dp_ack_seq =
                            cpu_to_be64(rds_ib_piggyb_ack(ic));
+                       dp->ricp_v6.dp_cmn.ricpc_dp_toss = conn->c_tos;
 
                        conn_param->private_data = &dp->ricp_v6;
                        conn_param->private_data_len = sizeof(dp->ricp_v6);
@@ -234,6 +237,7 @@ static void rds_ib_cm_fill_conn_param(struct rds_connection *conn,
                            cpu_to_be16(RDS_IB_SUPPORTED_PROTOCOLS);
                        dp->ricp_v4.dp_ack_seq =
                            cpu_to_be64(rds_ib_piggyb_ack(ic));
+                       dp->ricp_v4.dp_cmn.ricpc_dp_toss = conn->c_tos;
 
                        conn_param->private_data = &dp->ricp_v4;
                        conn_param->private_data_len = sizeof(dp->ricp_v4);
@@ -389,10 +393,9 @@ static void rds_ib_qp_event_handler(struct ib_event *event, void *data)
                rdma_notify(ic->i_cm_id, IB_EVENT_COMM_EST);
                break;
        default:
-               rdsdebug("Fatal QP Event %u (%s) "
-                       "- connection %pI6c->%pI6c, reconnecting\n",
-                       event->event, ib_event_msg(event->event),
-                       &conn->c_laddr, &conn->c_faddr);
+               rdsdebug("Fatal QP Event %u (%s) - connection %pI6c->%pI6c, reconnecting\n",
+                        event->event, ib_event_msg(event->event),
+                        &conn->c_laddr, &conn->c_faddr);
                rds_conn_drop(conn);
                break;
        }
@@ -660,13 +663,16 @@ static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event, bool isv6)
 
        /* Even if len is crap *now* I still want to check it. -ASG */
        if (event->param.conn.private_data_len < data_len || major == 0)
-               return RDS_PROTOCOL_3_0;
+               return RDS_PROTOCOL_4_0;
 
        common = be16_to_cpu(mask) & RDS_IB_SUPPORTED_PROTOCOLS;
-       if (major == 3 && common) {
-               version = RDS_PROTOCOL_3_0;
+       if (major == 4 && common) {
+               version = RDS_PROTOCOL_4_0;
                while ((common >>= 1) != 0)
                        version++;
+       } else if (RDS_PROTOCOL_COMPAT_VERSION ==
+                  RDS_PROTOCOL(major, minor)) {
+               version = RDS_PROTOCOL_COMPAT_VERSION;
        } else {
                if (isv6)
                        printk_ratelimited(KERN_NOTICE "RDS: Connection from %pI6c using incompatible protocol version %u.%u\n",
@@ -729,8 +735,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
 
        /* Check whether the remote protocol version matches ours. */
        version = rds_ib_protocol_compatible(event, isv6);
-       if (!version)
+       if (!version) {
+               err = RDS_RDMA_REJ_INCOMPAT;
                goto out;
+       }
 
        dp = event->param.conn.private_data;
        if (isv6) {
@@ -771,15 +779,16 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
                daddr6 = &d_mapped_addr;
        }
 
-       rdsdebug("saddr %pI6c daddr %pI6c RDSv%u.%u lguid 0x%llx fguid "
-                "0x%llx\n", saddr6, daddr6,
-                RDS_PROTOCOL_MAJOR(version), RDS_PROTOCOL_MINOR(version),
+       rdsdebug("saddr %pI6c daddr %pI6c RDSv%u.%u lguid 0x%llx fguid 0x%llx, tos:%d\n",
+                saddr6, daddr6, RDS_PROTOCOL_MAJOR(version),
+                RDS_PROTOCOL_MINOR(version),
                 (unsigned long long)be64_to_cpu(lguid),
-                (unsigned long long)be64_to_cpu(fguid));
+                (unsigned long long)be64_to_cpu(fguid), dp_cmn->ricpc_dp_toss);
 
        /* RDS/IB is not currently netns aware, thus init_net */
        conn = rds_conn_create(&init_net, daddr6, saddr6,
-                              &rds_ib_transport, GFP_KERNEL, ifindex);
+                              &rds_ib_transport, dp_cmn->ricpc_dp_toss,
+                              GFP_KERNEL, ifindex);
        if (IS_ERR(conn)) {
                rdsdebug("rds_conn_create failed (%ld)\n", PTR_ERR(conn));
                conn = NULL;
@@ -846,7 +855,7 @@ out:
        if (conn)
                mutex_unlock(&conn->c_cm_lock);
        if (err)
-               rdma_reject(cm_id, NULL, 0);
+               rdma_reject(cm_id, &err, sizeof(int));
        return destroy;
 }
 
@@ -861,7 +870,7 @@ int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id, bool isv6)
 
        /* If the peer doesn't do protocol negotiation, we must
         * default to RDSv3.0 */
-       rds_ib_set_protocol(conn, RDS_PROTOCOL_3_0);
+       rds_ib_set_protocol(conn, RDS_PROTOCOL_4_1);
        ic->i_flowctl = rds_ib_sysctl_flow_control;     /* advertise flow control */
 
        ret = rds_ib_setup_qp(conn);
@@ -870,7 +879,8 @@ int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id, bool isv6)
                goto out;
        }
 
-       rds_ib_cm_fill_conn_param(conn, &conn_param, &dp, RDS_PROTOCOL_VERSION,
+       rds_ib_cm_fill_conn_param(conn, &conn_param, &dp,
+                                 conn->c_proposed_version,
                                  UINT_MAX, UINT_MAX, isv6);
        ret = rdma_connect(cm_id, &conn_param);
        if (ret)
index 2f16146..d395eec 100644 (file)
@@ -986,9 +986,9 @@ void rds_ib_recv_cqe_handler(struct rds_ib_connection *ic,
        } else {
                /* We expect errors as the qp is drained during shutdown */
                if (rds_conn_up(conn) || rds_conn_connecting(conn))
-                       rds_ib_conn_error(conn, "recv completion on <%pI6c,%pI6c> had status %u (%s), disconnecting and reconnecting\n",
+                       rds_ib_conn_error(conn, "recv completion on <%pI6c,%pI6c, %d> had status %u (%s), disconnecting and reconnecting\n",
                                          &conn->c_laddr, &conn->c_faddr,
-                                         wc->status,
+                                         conn->c_tos, wc->status,
                                          ib_wc_status_msg(wc->status));
        }
 
index 4e0c36a..09c46f2 100644 (file)
@@ -305,8 +305,9 @@ void rds_ib_send_cqe_handler(struct rds_ib_connection *ic, struct ib_wc *wc)
 
        /* We expect errors as the qp is drained during shutdown */
        if (wc->status != IB_WC_SUCCESS && rds_conn_up(conn)) {
-               rds_ib_conn_error(conn, "send completion on <%pI6c,%pI6c> had status %u (%s), disconnecting and reconnecting\n",
-                                 &conn->c_laddr, &conn->c_faddr, wc->status,
+               rds_ib_conn_error(conn, "send completion on <%pI6c,%pI6c,%d> had status %u (%s), disconnecting and reconnecting\n",
+                                 &conn->c_laddr, &conn->c_faddr,
+                                 conn->c_tos, wc->status,
                                  ib_wc_status_msg(wc->status));
        }
 }
index 6b0f57c..46bce83 100644 (file)
@@ -51,6 +51,8 @@ static int rds_rdma_cm_event_handler_cmn(struct rdma_cm_id *cm_id,
        struct rds_connection *conn = cm_id->context;
        struct rds_transport *trans;
        int ret = 0;
+       int *err;
+       u8 len;
 
        rdsdebug("conn %p id %p handling event %u (%s)\n", conn, cm_id,
                 event->event, rdma_event_msg(event->event));
@@ -81,6 +83,7 @@ static int rds_rdma_cm_event_handler_cmn(struct rdma_cm_id *cm_id,
                break;
 
        case RDMA_CM_EVENT_ADDR_RESOLVED:
+               rdma_set_service_type(cm_id, conn->c_tos);
                /* XXX do we need to clean up if this fails? */
                ret = rdma_resolve_route(cm_id,
                                         RDS_RDMA_RESOLVE_TIMEOUT_MS);
@@ -106,8 +109,19 @@ static int rds_rdma_cm_event_handler_cmn(struct rdma_cm_id *cm_id,
                break;
 
        case RDMA_CM_EVENT_REJECTED:
+               if (!conn)
+                       break;
+               err = (int *)rdma_consumer_reject_data(cm_id, event, &len);
+               if (!err || (err && ((*err) == RDS_RDMA_REJ_INCOMPAT))) {
+                       pr_warn("RDS/RDMA: conn <%pI6c, %pI6c> rejected, dropping connection\n",
+                               &conn->c_laddr, &conn->c_faddr);
+                       conn->c_proposed_version = RDS_PROTOCOL_COMPAT_VERSION;
+                       conn->c_tos = 0;
+                       rds_conn_drop(conn);
+               }
                rdsdebug("Connection rejected: %s\n",
                         rdma_reject_msg(cm_id, event->status));
+               break;
                /* FALLTHROUGH */
        case RDMA_CM_EVENT_ADDR_ERROR:
        case RDMA_CM_EVENT_ROUTE_ERROR:
index 200d313..bfafd4a 100644 (file)
 
 #define RDS_RDMA_RESOLVE_TIMEOUT_MS     5000
 
+/* Below reject reason is for legacy interoperability issue with non-linux
+ * RDS endpoints where older version incompatibility is conveyed via value 1.
+ * For future version(s), proper encoded reject reason should be be used.
+ */
+#define RDS_RDMA_REJ_INCOMPAT          1
+
 int rds_rdma_conn_connect(struct rds_connection *conn);
 int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
                              struct rdma_cm_event *event);
index 4ffe100..0d8f67c 100644 (file)
  */
 #define RDS_PROTOCOL_3_0       0x0300
 #define RDS_PROTOCOL_3_1       0x0301
+#define RDS_PROTOCOL_4_0       0x0400
+#define RDS_PROTOCOL_4_1       0x0401
 #define RDS_PROTOCOL_VERSION   RDS_PROTOCOL_3_1
 #define RDS_PROTOCOL_MAJOR(v)  ((v) >> 8)
 #define RDS_PROTOCOL_MINOR(v)  ((v) & 255)
 #define RDS_PROTOCOL(maj, min) (((maj) << 8) | min)
+#define RDS_PROTOCOL_COMPAT_VERSION    RDS_PROTOCOL_3_1
 
 /* The following ports, 16385, 18634, 18635, are registered with IANA as
  * the ports to be used for RDS over TCP and UDP.  Currently, only RDS over
@@ -151,9 +154,13 @@ struct rds_connection {
        struct rds_cong_map     *c_fcong;
 
        /* Protocol version */
+       unsigned int            c_proposed_version;
        unsigned int            c_version;
        possible_net_t          c_net;
 
+       /* TOS */
+       u8                      c_tos;
+
        struct list_head        c_map_item;
        unsigned long           c_map_queued;
 
@@ -567,6 +574,7 @@ struct rds_transport {
        void (*free_mr)(void *trans_private, int invalidate);
        void (*flush_mrs)(void);
        bool (*t_unloading)(struct rds_connection *conn);
+       u8 (*get_tos_map)(u8 tos);
 };
 
 /* Bind hash table key length.  It is the sum of the size of a struct
@@ -648,6 +656,7 @@ struct rds_sock {
        u8                      rs_rx_traces;
        u8                      rs_rx_trace[RDS_MSG_RX_DGRAM_TRACE_MAX];
        struct rds_msg_zcopy_queue rs_zcookie_queue;
+       u8                      rs_tos;
 };
 
 static inline struct rds_sock *rds_sk_to_rs(const struct sock *sk)
@@ -756,13 +765,14 @@ void rds_conn_exit(void);
 struct rds_connection *rds_conn_create(struct net *net,
                                       const struct in6_addr *laddr,
                                       const struct in6_addr *faddr,
-                                      struct rds_transport *trans, gfp_t gfp,
+                                      struct rds_transport *trans,
+                                      u8 tos, gfp_t gfp,
                                       int dev_if);
 struct rds_connection *rds_conn_create_outgoing(struct net *net,
                                                const struct in6_addr *laddr,
                                                const struct in6_addr *faddr,
                                                struct rds_transport *trans,
-                                               gfp_t gfp, int dev_if);
+                                               u8 tos, gfp_t gfp, int dev_if);
 void rds_conn_shutdown(struct rds_conn_path *cpath);
 void rds_conn_destroy(struct rds_connection *conn);
 void rds_conn_drop(struct rds_connection *conn);
index 727639d..853de48 100644 (file)
@@ -549,9 +549,21 @@ static int rds_cmsg_recv(struct rds_incoming *inc, struct msghdr *msg,
 
        if ((inc->i_rx_tstamp != 0) &&
            sock_flag(rds_rs_to_sk(rs), SOCK_RCVTSTAMP)) {
-               struct timeval tv = ktime_to_timeval(inc->i_rx_tstamp);
-               ret = put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP,
-                              sizeof(tv), &tv);
+               struct __kernel_old_timeval tv = ns_to_kernel_old_timeval(inc->i_rx_tstamp);
+
+               if (!sock_flag(rds_rs_to_sk(rs), SOCK_TSTAMP_NEW)) {
+                       ret = put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
+                                      sizeof(tv), &tv);
+               } else {
+                       struct __kernel_sock_timeval sk_tv;
+
+                       sk_tv.tv_sec = tv.tv_sec;
+                       sk_tv.tv_usec = tv.tv_usec;
+
+                       ret = put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_NEW,
+                                      sizeof(sk_tv), &sk_tv);
+               }
+
                if (ret)
                        goto out;
        }
@@ -770,6 +782,7 @@ void rds_inc_info_copy(struct rds_incoming *inc,
 
        minfo.seq = be64_to_cpu(inc->i_hdr.h_sequence);
        minfo.len = be32_to_cpu(inc->i_hdr.h_len);
+       minfo.tos = inc->i_conn->c_tos;
 
        if (flip) {
                minfo.laddr = daddr;
index fd8b687..166dd57 100644 (file)
@@ -1277,12 +1277,13 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
 
        /* rds_conn_create has a spinlock that runs with IRQ off.
         * Caching the conn in the socket helps a lot. */
-       if (rs->rs_conn && ipv6_addr_equal(&rs->rs_conn->c_faddr, &daddr))
+       if (rs->rs_conn && ipv6_addr_equal(&rs->rs_conn->c_faddr, &daddr) &&
+           rs->rs_tos == rs->rs_conn->c_tos) {
                conn = rs->rs_conn;
-       else {
+       else {
                conn = rds_conn_create_outgoing(sock_net(sock->sk),
                                                &rs->rs_bound_addr, &daddr,
-                                               rs->rs_transport,
+                                               rs->rs_transport, rs->rs_tos,
                                                sock->sk->sk_allocation,
                                                scope_id);
                if (IS_ERR(conn)) {
index c16f0a3..fd26941 100644 (file)
@@ -267,6 +267,7 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len,
                tsinfo.last_sent_nxt = tc->t_last_sent_nxt;
                tsinfo.last_expected_una = tc->t_last_expected_una;
                tsinfo.last_seen_una = tc->t_last_seen_una;
+               tsinfo.tos = tc->t_cpath->cp_conn->c_tos;
 
                rds_info_copy(iter, &tsinfo, sizeof(tsinfo));
        }
@@ -452,6 +453,12 @@ static void rds_tcp_destroy_conns(void)
 
 static void rds_tcp_exit(void);
 
+static u8 rds_tcp_get_tos_map(u8 tos)
+{
+       /* all user tos mapped to default 0 for TCP transport */
+       return 0;
+}
+
 struct rds_transport rds_tcp_transport = {
        .laddr_check            = rds_tcp_laddr_check,
        .xmit_path_prepare      = rds_tcp_xmit_path_prepare,
@@ -466,6 +473,7 @@ struct rds_transport rds_tcp_transport = {
        .inc_free               = rds_tcp_inc_free,
        .stats_info_copy        = rds_tcp_stats_info_copy,
        .exit                   = rds_tcp_exit,
+       .get_tos_map            = rds_tcp_get_tos_map,
        .t_owner                = THIS_MODULE,
        .t_name                 = "tcp",
        .t_type                 = RDS_TRANS_TCP,
index c12203f..810a3a4 100644 (file)
@@ -200,7 +200,7 @@ int rds_tcp_accept_one(struct socket *sock)
 
        conn = rds_conn_create(sock_net(sock->sk),
                               my_addr, peer_addr,
-                              &rds_tcp_transport, GFP_KERNEL, dev_if);
+                              &rds_tcp_transport, 0, GFP_KERNEL, dev_if);
 
        if (IS_ERR(conn)) {
                ret = PTR_ERR(conn);
index e64f9e4..32dc50f 100644 (file)
@@ -93,6 +93,7 @@ void rds_connect_path_complete(struct rds_conn_path *cp, int curr)
                queue_delayed_work(rds_wq, &cp->cp_recv_w, 0);
        }
        rcu_read_unlock();
+       cp->cp_conn->c_proposed_version = RDS_PROTOCOL_VERSION;
 }
 EXPORT_SYMBOL_GPL(rds_connect_path_complete);
 
index 77e9f85..f2ff21d 100644 (file)
@@ -850,6 +850,7 @@ void rose_link_device_down(struct net_device *dev)
 
 /*
  *     Route a frame to an appropriate AX.25 connection.
+ *     A NULL ax25_cb indicates an internally generated frame.
  */
 int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
 {
@@ -867,6 +868,10 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
 
        if (skb->len < ROSE_MIN_LEN)
                return res;
+
+       if (!ax25)
+               return rose_loopback_queue(skb, NULL);
+
        frametype = skb->data[2];
        lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
        if (frametype == ROSE_CALL_REQUEST &&
index 0906e51..15cf42d 100644 (file)
@@ -202,7 +202,7 @@ static int rxrpc_open_socket(struct rxrpc_local *local, struct net *net)
 
                /* We want receive timestamps. */
                opt = 1;
-               ret = kernel_setsockopt(local->socket, SOL_SOCKET, SO_TIMESTAMPNS,
+               ret = kernel_setsockopt(local->socket, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
                                        (char *)&opt, sizeof(opt));
                if (ret < 0) {
                        _debug("setsockopt failed");
index eaf19eb..3f7bb11 100644 (file)
@@ -596,6 +596,7 @@ error_requeue_call:
        }
 error_no_call:
        release_sock(&rx->sk);
+error_trace:
        trace_rxrpc_recvmsg(call, rxrpc_recvmsg_return, 0, 0, 0, ret);
        return ret;
 
@@ -604,7 +605,7 @@ wait_interrupted:
 wait_error:
        finish_wait(sk_sleep(&rx->sk), &wait);
        call = NULL;
-       goto error_no_call;
+       goto error_trace;
 }
 
 /**
index d4b8355..aecf1bf 100644 (file)
@@ -543,7 +543,7 @@ int tcf_register_action(struct tc_action_ops *act,
 
        write_lock(&act_mod_lock);
        list_for_each_entry(a, &act_base, head) {
-               if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
+               if (act->id == a->id || (strcmp(act->kind, a->kind) == 0)) {
                        write_unlock(&act_mod_lock);
                        unregister_pernet_subsys(ops);
                        return -EEXIST;
index c763384..aa5c38d 100644 (file)
@@ -396,7 +396,7 @@ static int tcf_bpf_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_bpf_ops __read_mostly = {
        .kind           =       "bpf",
-       .type           =       TCA_ACT_BPF,
+       .id             =       TCA_ID_BPF,
        .owner          =       THIS_MODULE,
        .act            =       tcf_bpf_act,
        .dump           =       tcf_bpf_dump,
index 8475913..5d24993 100644 (file)
@@ -204,7 +204,7 @@ static int tcf_connmark_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_connmark_ops = {
        .kind           =       "connmark",
-       .type           =       TCA_ACT_CONNMARK,
+       .id             =       TCA_ID_CONNMARK,
        .owner          =       THIS_MODULE,
        .act            =       tcf_connmark_act,
        .dump           =       tcf_connmark_dump,
index 3dc25b7..945fb34 100644 (file)
@@ -660,7 +660,7 @@ static size_t tcf_csum_get_fill_size(const struct tc_action *act)
 
 static struct tc_action_ops act_csum_ops = {
        .kind           = "csum",
-       .type           = TCA_ACT_CSUM,
+       .id             = TCA_ID_CSUM,
        .owner          = THIS_MODULE,
        .act            = tcf_csum_act,
        .dump           = tcf_csum_dump,
index b61c20e..93da000 100644 (file)
@@ -253,7 +253,7 @@ static size_t tcf_gact_get_fill_size(const struct tc_action *act)
 
 static struct tc_action_ops act_gact_ops = {
        .kind           =       "gact",
-       .type           =       TCA_ACT_GACT,
+       .id             =       TCA_ID_GACT,
        .owner          =       THIS_MODULE,
        .act            =       tcf_gact_act,
        .stats_update   =       tcf_gact_stats_update,
index 30b63fa..9b1f2b3 100644 (file)
@@ -864,7 +864,7 @@ static int tcf_ife_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_ife_ops = {
        .kind = "ife",
-       .type = TCA_ACT_IFE,
+       .id = TCA_ID_IFE,
        .owner = THIS_MODULE,
        .act = tcf_ife_act,
        .dump = tcf_ife_dump,
index 8af6c11..1bad190 100644 (file)
@@ -338,7 +338,7 @@ static int tcf_ipt_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_ipt_ops = {
        .kind           =       "ipt",
-       .type           =       TCA_ACT_IPT,
+       .id             =       TCA_ID_IPT,
        .owner          =       THIS_MODULE,
        .act            =       tcf_ipt_act,
        .dump           =       tcf_ipt_dump,
@@ -387,7 +387,7 @@ static int tcf_xt_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_xt_ops = {
        .kind           =       "xt",
-       .type           =       TCA_ACT_XT,
+       .id             =       TCA_ID_XT,
        .owner          =       THIS_MODULE,
        .act            =       tcf_ipt_act,
        .dump           =       tcf_ipt_dump,
index c8cf4d1..6692fd0 100644 (file)
@@ -400,7 +400,7 @@ static void tcf_mirred_put_dev(struct net_device *dev)
 
 static struct tc_action_ops act_mirred_ops = {
        .kind           =       "mirred",
-       .type           =       TCA_ACT_MIRRED,
+       .id             =       TCA_ID_MIRRED,
        .owner          =       THIS_MODULE,
        .act            =       tcf_mirred_act,
        .stats_update   =       tcf_stats_update,
index c5c1e23..543eab9 100644 (file)
@@ -304,7 +304,7 @@ static int tcf_nat_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_nat_ops = {
        .kind           =       "nat",
-       .type           =       TCA_ACT_NAT,
+       .id             =       TCA_ID_NAT,
        .owner          =       THIS_MODULE,
        .act            =       tcf_nat_act,
        .dump           =       tcf_nat_dump,
index 2b372a0..a803738 100644 (file)
@@ -406,7 +406,7 @@ static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,
        struct tcf_t t;
        int s;
 
-       s = sizeof(*opt) + p->tcfp_nkeys * sizeof(struct tc_pedit_key);
+       s = struct_size(opt, keys, p->tcfp_nkeys);
 
        /* netlink spinlocks held above us - must use ATOMIC */
        opt = kzalloc(s, GFP_ATOMIC);
@@ -470,7 +470,7 @@ static int tcf_pedit_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_pedit_ops = {
        .kind           =       "pedit",
-       .type           =       TCA_ACT_PEDIT,
+       .id             =       TCA_ID_PEDIT,
        .owner          =       THIS_MODULE,
        .act            =       tcf_pedit_act,
        .dump           =       tcf_pedit_dump,
index ec8ec55..8271a62 100644 (file)
@@ -366,7 +366,7 @@ MODULE_LICENSE("GPL");
 
 static struct tc_action_ops act_police_ops = {
        .kind           =       "police",
-       .type           =       TCA_ID_POLICE,
+       .id             =       TCA_ID_POLICE,
        .owner          =       THIS_MODULE,
        .act            =       tcf_police_act,
        .dump           =       tcf_police_dump,
index 1a0c682..203e399 100644 (file)
@@ -233,7 +233,7 @@ static int tcf_sample_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_sample_ops = {
        .kind     = "sample",
-       .type     = TCA_ACT_SAMPLE,
+       .id       = TCA_ID_SAMPLE,
        .owner    = THIS_MODULE,
        .act      = tcf_sample_act,
        .dump     = tcf_sample_dump,
index 902957b..d54cb60 100644 (file)
@@ -19,8 +19,6 @@
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
 
-#define TCA_ACT_SIMP 22
-
 #include <linux/tc_act/tc_defact.h>
 #include <net/tc_act/tc_defact.h>
 
@@ -197,7 +195,7 @@ static int tcf_simp_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_simp_ops = {
        .kind           =       "simple",
-       .type           =       TCA_ACT_SIMP,
+       .id             =       TCA_ID_SIMP,
        .owner          =       THIS_MODULE,
        .act            =       tcf_simp_act,
        .dump           =       tcf_simp_dump,
index 64dba37..39f8a67 100644 (file)
@@ -305,7 +305,7 @@ static int tcf_skbedit_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_skbedit_ops = {
        .kind           =       "skbedit",
-       .type           =       TCA_ACT_SKBEDIT,
+       .id             =       TCA_ID_SKBEDIT,
        .owner          =       THIS_MODULE,
        .act            =       tcf_skbedit_act,
        .dump           =       tcf_skbedit_dump,
index 59710a1..7bac1d7 100644 (file)
@@ -260,7 +260,7 @@ static int tcf_skbmod_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_skbmod_ops = {
        .kind           =       "skbmod",
-       .type           =       TCA_ACT_SKBMOD,
+       .id             =       TCA_ACT_SKBMOD,
        .owner          =       THIS_MODULE,
        .act            =       tcf_skbmod_act,
        .dump           =       tcf_skbmod_dump,
index 8b43fe0..9104b8e 100644 (file)
@@ -563,7 +563,7 @@ static int tunnel_key_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_tunnel_key_ops = {
        .kind           =       "tunnel_key",
-       .type           =       TCA_ACT_TUNNEL_KEY,
+       .id             =       TCA_ID_TUNNEL_KEY,
        .owner          =       THIS_MODULE,
        .act            =       tunnel_key_act,
        .dump           =       tunnel_key_dump,
index 93fdaf7..ac00615 100644 (file)
@@ -297,7 +297,7 @@ static int tcf_vlan_search(struct net *net, struct tc_action **a, u32 index)
 
 static struct tc_action_ops act_vlan_ops = {
        .kind           =       "vlan",
-       .type           =       TCA_ACT_VLAN,
+       .id             =       TCA_ID_VLAN,
        .owner          =       THIS_MODULE,
        .act            =       tcf_vlan_act,
        .dump           =       tcf_vlan_dump,
index e2b5cb2..9ad5389 100644 (file)
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
 #include <net/pkt_cls.h>
+#include <net/tc_act/tc_pedit.h>
+#include <net/tc_act/tc_mirred.h>
+#include <net/tc_act/tc_vlan.h>
+#include <net/tc_act/tc_tunnel_key.h>
+#include <net/tc_act/tc_csum.h>
+#include <net/tc_act/tc_gact.h>
+#include <net/tc_act/tc_skbedit.h>
 
 extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1];
 
@@ -61,7 +68,8 @@ static const struct tcf_proto_ops *__tcf_proto_lookup_ops(const char *kind)
 }
 
 static const struct tcf_proto_ops *
-tcf_proto_lookup_ops(const char *kind, struct netlink_ext_ack *extack)
+tcf_proto_lookup_ops(const char *kind, bool rtnl_held,
+                    struct netlink_ext_ack *extack)
 {
        const struct tcf_proto_ops *ops;
 
@@ -69,9 +77,11 @@ tcf_proto_lookup_ops(const char *kind, struct netlink_ext_ack *extack)
        if (ops)
                return ops;
 #ifdef CONFIG_MODULES
-       rtnl_unlock();
+       if (rtnl_held)
+               rtnl_unlock();
        request_module("cls_%s", kind);
-       rtnl_lock();
+       if (rtnl_held)
+               rtnl_lock();
        ops = __tcf_proto_lookup_ops(kind);
        /* We dropped the RTNL semaphore in order to perform
         * the module load. So, even if we succeeded in loading
@@ -152,8 +162,26 @@ static inline u32 tcf_auto_prio(struct tcf_proto *tp)
        return TC_H_MAJ(first);
 }
 
+static bool tcf_proto_is_unlocked(const char *kind)
+{
+       const struct tcf_proto_ops *ops;
+       bool ret;
+
+       ops = tcf_proto_lookup_ops(kind, false, NULL);
+       /* On error return false to take rtnl lock. Proto lookup/create
+        * functions will perform lookup again and properly handle errors.
+        */
+       if (IS_ERR(ops))
+               return false;
+
+       ret = !!(ops->flags & TCF_PROTO_OPS_DOIT_UNLOCKED);
+       module_put(ops->owner);
+       return ret;
+}
+
 static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
                                          u32 prio, struct tcf_chain *chain,
+                                         bool rtnl_held,
                                          struct netlink_ext_ack *extack)
 {
        struct tcf_proto *tp;
@@ -163,7 +191,7 @@ static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
        if (!tp)
                return ERR_PTR(-ENOBUFS);
 
-       tp->ops = tcf_proto_lookup_ops(kind, extack);
+       tp->ops = tcf_proto_lookup_ops(kind, rtnl_held, extack);
        if (IS_ERR(tp->ops)) {
                err = PTR_ERR(tp->ops);
                goto errout;
@@ -172,6 +200,8 @@ static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
        tp->protocol = protocol;
        tp->prio = prio;
        tp->chain = chain;
+       spin_lock_init(&tp->lock);
+       refcount_set(&tp->refcnt, 1);
 
        err = tp->ops->init(tp);
        if (err) {
@@ -185,14 +215,75 @@ errout:
        return ERR_PTR(err);
 }
 
-static void tcf_proto_destroy(struct tcf_proto *tp,
+static void tcf_proto_get(struct tcf_proto *tp)
+{
+       refcount_inc(&tp->refcnt);
+}
+
+static void tcf_chain_put(struct tcf_chain *chain);
+
+static void tcf_proto_destroy(struct tcf_proto *tp, bool rtnl_held,
                              struct netlink_ext_ack *extack)
 {
-       tp->ops->destroy(tp, extack);
+       tp->ops->destroy(tp, rtnl_held, extack);
+       tcf_chain_put(tp->chain);
        module_put(tp->ops->owner);
        kfree_rcu(tp, rcu);
 }
 
+static void tcf_proto_put(struct tcf_proto *tp, bool rtnl_held,
+                         struct netlink_ext_ack *extack)
+{
+       if (refcount_dec_and_test(&tp->refcnt))
+               tcf_proto_destroy(tp, rtnl_held, extack);
+}
+
+static int walker_noop(struct tcf_proto *tp, void *d, struct tcf_walker *arg)
+{
+       return -1;
+}
+
+static bool tcf_proto_is_empty(struct tcf_proto *tp, bool rtnl_held)
+{
+       struct tcf_walker walker = { .fn = walker_noop, };
+
+       if (tp->ops->walk) {
+               tp->ops->walk(tp, &walker, rtnl_held);
+               return !walker.stop;
+       }
+       return true;
+}
+
+static bool tcf_proto_check_delete(struct tcf_proto *tp, bool rtnl_held)
+{
+       spin_lock(&tp->lock);
+       if (tcf_proto_is_empty(tp, rtnl_held))
+               tp->deleting = true;
+       spin_unlock(&tp->lock);
+       return tp->deleting;
+}
+
+static void tcf_proto_mark_delete(struct tcf_proto *tp)
+{
+       spin_lock(&tp->lock);
+       tp->deleting = true;
+       spin_unlock(&tp->lock);
+}
+
+static bool tcf_proto_is_deleting(struct tcf_proto *tp)
+{
+       bool deleting;
+
+       spin_lock(&tp->lock);
+       deleting = tp->deleting;
+       spin_unlock(&tp->lock);
+
+       return deleting;
+}
+
+#define ASSERT_BLOCK_LOCKED(block)                                     \
+       lockdep_assert_held(&(block)->lock)
+
 struct tcf_filter_chain_list_item {
        struct list_head list;
        tcf_chain_head_change_t *chain_head_change;
@@ -204,10 +295,13 @@ static struct tcf_chain *tcf_chain_create(struct tcf_block *block,
 {
        struct tcf_chain *chain;
 
+       ASSERT_BLOCK_LOCKED(block);
+
        chain = kzalloc(sizeof(*chain), GFP_KERNEL);
        if (!chain)
                return NULL;
        list_add_tail(&chain->list, &block->chain_list);
+       mutex_init(&chain->filter_chain_lock);
        chain->block = block;
        chain->index = chain_index;
        chain->refcnt = 1;
@@ -231,29 +325,59 @@ static void tcf_chain0_head_change(struct tcf_chain *chain,
 
        if (chain->index)
                return;
+
+       mutex_lock(&block->lock);
        list_for_each_entry(item, &block->chain0.filter_chain_list, list)
                tcf_chain_head_change_item(item, tp_head);
+       mutex_unlock(&block->lock);
 }
 
-static void tcf_chain_destroy(struct tcf_chain *chain)
+/* Returns true if block can be safely freed. */
+
+static bool tcf_chain_detach(struct tcf_chain *chain)
 {
        struct tcf_block *block = chain->block;
 
+       ASSERT_BLOCK_LOCKED(block);
+
        list_del(&chain->list);
        if (!chain->index)
                block->chain0.chain = NULL;
+
+       if (list_empty(&block->chain_list) &&
+           refcount_read(&block->refcnt) == 0)
+               return true;
+
+       return false;
+}
+
+static void tcf_block_destroy(struct tcf_block *block)
+{
+       mutex_destroy(&block->lock);
+       kfree_rcu(block, rcu);
+}
+
+static void tcf_chain_destroy(struct tcf_chain *chain, bool free_block)
+{
+       struct tcf_block *block = chain->block;
+
+       mutex_destroy(&chain->filter_chain_lock);
        kfree(chain);
-       if (list_empty(&block->chain_list) && !refcount_read(&block->refcnt))
-               kfree_rcu(block, rcu);
+       if (free_block)
+               tcf_block_destroy(block);
 }
 
 static void tcf_chain_hold(struct tcf_chain *chain)
 {
+       ASSERT_BLOCK_LOCKED(chain->block);
+
        ++chain->refcnt;
 }
 
 static bool tcf_chain_held_by_acts_only(struct tcf_chain *chain)
 {
+       ASSERT_BLOCK_LOCKED(chain->block);
+
        /* In case all the references are action references, this
         * chain should not be shown to the user.
         */
@@ -265,6 +389,8 @@ static struct tcf_chain *tcf_chain_lookup(struct tcf_block *block,
 {
        struct tcf_chain *chain;
 
+       ASSERT_BLOCK_LOCKED(block);
+
        list_for_each_entry(chain, &block->chain_list, list) {
                if (chain->index == chain_index)
                        return chain;
@@ -279,31 +405,40 @@ static struct tcf_chain *__tcf_chain_get(struct tcf_block *block,
                                         u32 chain_index, bool create,
                                         bool by_act)
 {
-       struct tcf_chain *chain = tcf_chain_lookup(block, chain_index);
+       struct tcf_chain *chain = NULL;
+       bool is_first_reference;
 
+       mutex_lock(&block->lock);
+       chain = tcf_chain_lookup(block, chain_index);
        if (chain) {
                tcf_chain_hold(chain);
        } else {
                if (!create)
-                       return NULL;
+                       goto errout;
                chain = tcf_chain_create(block, chain_index);
                if (!chain)
-                       return NULL;
+                       goto errout;
        }
 
        if (by_act)
                ++chain->action_refcnt;
+       is_first_reference = chain->refcnt - chain->action_refcnt == 1;
+       mutex_unlock(&block->lock);
 
        /* Send notification only in case we got the first
         * non-action reference. Until then, the chain acts only as
         * a placeholder for actions pointing to it and user ought
         * not know about them.
         */
-       if (chain->refcnt - chain->action_refcnt == 1 && !by_act)
+       if (is_first_reference && !by_act)
                tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
                                RTM_NEWCHAIN, false);
 
        return chain;
+
+errout:
+       mutex_unlock(&block->lock);
+       return chain;
 }
 
 static struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index,
@@ -318,51 +453,94 @@ struct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block, u32 chain_index)
 }
 EXPORT_SYMBOL(tcf_chain_get_by_act);
 
-static void tc_chain_tmplt_del(struct tcf_chain *chain);
+static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops,
+                              void *tmplt_priv);
+static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
+                                 void *tmplt_priv, u32 chain_index,
+                                 struct tcf_block *block, struct sk_buff *oskb,
+                                 u32 seq, u16 flags, bool unicast);
 
-static void __tcf_chain_put(struct tcf_chain *chain, bool by_act)
+static void __tcf_chain_put(struct tcf_chain *chain, bool by_act,
+                           bool explicitly_created)
 {
+       struct tcf_block *block = chain->block;
+       const struct tcf_proto_ops *tmplt_ops;
+       bool is_last, free_block = false;
+       unsigned int refcnt;
+       void *tmplt_priv;
+       u32 chain_index;
+
+       mutex_lock(&block->lock);
+       if (explicitly_created) {
+               if (!chain->explicitly_created) {
+                       mutex_unlock(&block->lock);
+                       return;
+               }
+               chain->explicitly_created = false;
+       }
+
        if (by_act)
                chain->action_refcnt--;
-       chain->refcnt--;
+
+       /* tc_chain_notify_delete can't be called while holding block lock.
+        * However, when block is unlocked chain can be changed concurrently, so
+        * save these to temporary variables.
+        */
+       refcnt = --chain->refcnt;
+       is_last = refcnt - chain->action_refcnt == 0;
+       tmplt_ops = chain->tmplt_ops;
+       tmplt_priv = chain->tmplt_priv;
+       chain_index = chain->index;
+
+       if (refcnt == 0)
+               free_block = tcf_chain_detach(chain);
+       mutex_unlock(&block->lock);
 
        /* The last dropped non-action reference will trigger notification. */
-       if (chain->refcnt - chain->action_refcnt == 0 && !by_act)
-               tc_chain_notify(chain, NULL, 0, 0, RTM_DELCHAIN, false);
+       if (is_last && !by_act) {
+               tc_chain_notify_delete(tmplt_ops, tmplt_priv, chain_index,
+                                      block, NULL, 0, 0, false);
+               /* Last reference to chain, no need to lock. */
+               chain->flushing = false;
+       }
 
-       if (chain->refcnt == 0) {
-               tc_chain_tmplt_del(chain);
-               tcf_chain_destroy(chain);
+       if (refcnt == 0) {
+               tc_chain_tmplt_del(tmplt_ops, tmplt_priv);
+               tcf_chain_destroy(chain, free_block);
        }
 }
 
 static void tcf_chain_put(struct tcf_chain *chain)
 {
-       __tcf_chain_put(chain, false);
+       __tcf_chain_put(chain, false, false);
 }
 
 void tcf_chain_put_by_act(struct tcf_chain *chain)
 {
-       __tcf_chain_put(chain, true);
+       __tcf_chain_put(chain, true, false);
 }
 EXPORT_SYMBOL(tcf_chain_put_by_act);
 
 static void tcf_chain_put_explicitly_created(struct tcf_chain *chain)
 {
-       if (chain->explicitly_created)
-               tcf_chain_put(chain);
+       __tcf_chain_put(chain, false, true);
 }
 
-static void tcf_chain_flush(struct tcf_chain *chain)
+static void tcf_chain_flush(struct tcf_chain *chain, bool rtnl_held)
 {
-       struct tcf_proto *tp = rtnl_dereference(chain->filter_chain);
+       struct tcf_proto *tp, *tp_next;
 
+       mutex_lock(&chain->filter_chain_lock);
+       tp = tcf_chain_dereference(chain->filter_chain, chain);
+       RCU_INIT_POINTER(chain->filter_chain, NULL);
        tcf_chain0_head_change(chain, NULL);
+       chain->flushing = true;
+       mutex_unlock(&chain->filter_chain_lock);
+
        while (tp) {
-               RCU_INIT_POINTER(chain->filter_chain, tp->next);
-               tcf_proto_destroy(tp, NULL);
-               tp = rtnl_dereference(chain->filter_chain);
-               tcf_chain_put(chain);
+               tp_next = rcu_dereference_protected(tp->next, 1);
+               tcf_proto_put(tp, rtnl_held, NULL);
+               tp = tp_next;
        }
 }
 
@@ -684,8 +862,8 @@ tcf_chain0_head_change_cb_add(struct tcf_block *block,
                              struct tcf_block_ext_info *ei,
                              struct netlink_ext_ack *extack)
 {
-       struct tcf_chain *chain0 = block->chain0.chain;
        struct tcf_filter_chain_list_item *item;
+       struct tcf_chain *chain0;
 
        item = kmalloc(sizeof(*item), GFP_KERNEL);
        if (!item) {
@@ -694,9 +872,32 @@ tcf_chain0_head_change_cb_add(struct tcf_block *block,
        }
        item->chain_head_change = ei->chain_head_change;
        item->chain_head_change_priv = ei->chain_head_change_priv;
-       if (chain0 && chain0->filter_chain)
-               tcf_chain_head_change_item(item, chain0->filter_chain);
-       list_add(&item->list, &block->chain0.filter_chain_list);
+
+       mutex_lock(&block->lock);
+       chain0 = block->chain0.chain;
+       if (chain0)
+               tcf_chain_hold(chain0);
+       else
+               list_add(&item->list, &block->chain0.filter_chain_list);
+       mutex_unlock(&block->lock);
+
+       if (chain0) {
+               struct tcf_proto *tp_head;
+
+               mutex_lock(&chain0->filter_chain_lock);
+
+               tp_head = tcf_chain_dereference(chain0->filter_chain, chain0);
+               if (tp_head)
+                       tcf_chain_head_change_item(item, tp_head);
+
+               mutex_lock(&block->lock);
+               list_add(&item->list, &block->chain0.filter_chain_list);
+               mutex_unlock(&block->lock);
+
+               mutex_unlock(&chain0->filter_chain_lock);
+               tcf_chain_put(chain0);
+       }
+
        return 0;
 }
 
@@ -704,20 +905,23 @@ static void
 tcf_chain0_head_change_cb_del(struct tcf_block *block,
                              struct tcf_block_ext_info *ei)
 {
-       struct tcf_chain *chain0 = block->chain0.chain;
        struct tcf_filter_chain_list_item *item;
 
+       mutex_lock(&block->lock);
        list_for_each_entry(item, &block->chain0.filter_chain_list, list) {
                if ((!ei->chain_head_change && !ei->chain_head_change_priv) ||
                    (item->chain_head_change == ei->chain_head_change &&
                     item->chain_head_change_priv == ei->chain_head_change_priv)) {
-                       if (chain0)
+                       if (block->chain0.chain)
                                tcf_chain_head_change_item(item, NULL);
                        list_del(&item->list);
+                       mutex_unlock(&block->lock);
+
                        kfree(item);
                        return;
                }
        }
+       mutex_unlock(&block->lock);
        WARN_ON(1);
 }
 
@@ -764,6 +968,7 @@ static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q,
                NL_SET_ERR_MSG(extack, "Memory allocation for block failed");
                return ERR_PTR(-ENOMEM);
        }
+       mutex_init(&block->lock);
        INIT_LIST_HEAD(&block->chain_list);
        INIT_LIST_HEAD(&block->cb_list);
        INIT_LIST_HEAD(&block->owner_list);
@@ -799,157 +1004,241 @@ static struct tcf_block *tcf_block_refcnt_get(struct net *net, u32 block_index)
        return block;
 }
 
-static void tcf_block_flush_all_chains(struct tcf_block *block)
+static struct tcf_chain *
+__tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain)
 {
-       struct tcf_chain *chain;
+       mutex_lock(&block->lock);
+       if (chain)
+               chain = list_is_last(&chain->list, &block->chain_list) ?
+                       NULL : list_next_entry(chain, list);
+       else
+               chain = list_first_entry_or_null(&block->chain_list,
+                                                struct tcf_chain, list);
 
-       /* Hold a refcnt for all chains, so that they don't disappear
-        * while we are iterating.
-        */
-       list_for_each_entry(chain, &block->chain_list, list)
+       /* skip all action-only chains */
+       while (chain && tcf_chain_held_by_acts_only(chain))
+               chain = list_is_last(&chain->list, &block->chain_list) ?
+                       NULL : list_next_entry(chain, list);
+
+       if (chain)
                tcf_chain_hold(chain);
+       mutex_unlock(&block->lock);
 
-       list_for_each_entry(chain, &block->chain_list, list)
-               tcf_chain_flush(chain);
+       return chain;
 }
 
-static void tcf_block_put_all_chains(struct tcf_block *block)
+/* Function to be used by all clients that want to iterate over all chains on
+ * block. It properly obtains block->lock and takes reference to chain before
+ * returning it. Users of this function must be tolerant to concurrent chain
+ * insertion/deletion or ensure that no concurrent chain modification is
+ * possible. Note that all netlink dump callbacks cannot guarantee to provide
+ * consistent dump because rtnl lock is released each time skb is filled with
+ * data and sent to user-space.
+ */
+
+struct tcf_chain *
+tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain)
 {
-       struct tcf_chain *chain, *tmp;
+       struct tcf_chain *chain_next = __tcf_get_next_chain(block, chain);
 
-       /* At this point, all the chains should have refcnt >= 1. */
-       list_for_each_entry_safe(chain, tmp, &block->chain_list, list) {
-               tcf_chain_put_explicitly_created(chain);
+       if (chain)
                tcf_chain_put(chain);
-       }
+
+       return chain_next;
 }
+EXPORT_SYMBOL(tcf_get_next_chain);
 
-static void __tcf_block_put(struct tcf_block *block, struct Qdisc *q,
-                           struct tcf_block_ext_info *ei)
+static struct tcf_proto *
+__tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp)
 {
-       if (refcount_dec_and_test(&block->refcnt)) {
-               /* Flushing/putting all chains will cause the block to be
-                * deallocated when last chain is freed. However, if chain_list
-                * is empty, block has to be manually deallocated. After block
-                * reference counter reached 0, it is no longer possible to
-                * increment it or add new chains to block.
-                */
-               bool free_block = list_empty(&block->chain_list);
+       u32 prio = 0;
 
-               if (tcf_block_shared(block))
-                       tcf_block_remove(block, block->net);
-               if (!free_block)
-                       tcf_block_flush_all_chains(block);
+       ASSERT_RTNL();
+       mutex_lock(&chain->filter_chain_lock);
 
-               if (q)
-                       tcf_block_offload_unbind(block, q, ei);
+       if (!tp) {
+               tp = tcf_chain_dereference(chain->filter_chain, chain);
+       } else if (tcf_proto_is_deleting(tp)) {
+               /* 'deleting' flag is set and chain->filter_chain_lock was
+                * unlocked, which means next pointer could be invalid. Restart
+                * search.
+                */
+               prio = tp->prio + 1;
+               tp = tcf_chain_dereference(chain->filter_chain, chain);
 
-               if (free_block)
-                       kfree_rcu(block, rcu);
-               else
-                       tcf_block_put_all_chains(block);
-       } else if (q) {
-               tcf_block_offload_unbind(block, q, ei);
+               for (; tp; tp = tcf_chain_dereference(tp->next, chain))
+                       if (!tp->deleting && tp->prio >= prio)
+                               break;
+       } else {
+               tp = tcf_chain_dereference(tp->next, chain);
        }
+
+       if (tp)
+               tcf_proto_get(tp);
+
+       mutex_unlock(&chain->filter_chain_lock);
+
+       return tp;
 }
 
-static void tcf_block_refcnt_put(struct tcf_block *block)
+/* Function to be used by all clients that want to iterate over all tp's on
+ * chain. Users of this function must be tolerant to concurrent tp
+ * insertion/deletion or ensure that no concurrent chain modification is
+ * possible. Note that all netlink dump callbacks cannot guarantee to provide
+ * consistent dump because rtnl lock is released each time skb is filled with
+ * data and sent to user-space.
+ */
+
+struct tcf_proto *
+tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp,
+                  bool rtnl_held)
 {
-       __tcf_block_put(block, NULL, NULL);
+       struct tcf_proto *tp_next = __tcf_get_next_proto(chain, tp);
+
+       if (tp)
+               tcf_proto_put(tp, rtnl_held, NULL);
+
+       return tp_next;
 }
+EXPORT_SYMBOL(tcf_get_next_proto);
 
-/* Find tcf block.
- * Set q, parent, cl when appropriate.
+static void tcf_block_flush_all_chains(struct tcf_block *block, bool rtnl_held)
+{
+       struct tcf_chain *chain;
+
+       /* Last reference to block. At this point chains cannot be added or
+        * removed concurrently.
+        */
+       for (chain = tcf_get_next_chain(block, NULL);
+            chain;
+            chain = tcf_get_next_chain(block, chain)) {
+               tcf_chain_put_explicitly_created(chain);
+               tcf_chain_flush(chain, rtnl_held);
+       }
+}
+
+/* Lookup Qdisc and increments its reference counter.
+ * Set parent, if necessary.
  */
 
-static struct tcf_block *tcf_block_find(struct net *net, struct Qdisc **q,
-                                       u32 *parent, unsigned long *cl,
-                                       int ifindex, u32 block_index,
-                                       struct netlink_ext_ack *extack)
+static int __tcf_qdisc_find(struct net *net, struct Qdisc **q,
+                           u32 *parent, int ifindex, bool rtnl_held,
+                           struct netlink_ext_ack *extack)
 {
-       struct tcf_block *block;
+       const struct Qdisc_class_ops *cops;
+       struct net_device *dev;
        int err = 0;
 
-       if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
-               block = tcf_block_refcnt_get(net, block_index);
-               if (!block) {
-                       NL_SET_ERR_MSG(extack, "Block of given index was not found");
-                       return ERR_PTR(-EINVAL);
-               }
-       } else {
-               const struct Qdisc_class_ops *cops;
-               struct net_device *dev;
-
-               rcu_read_lock();
+       if (ifindex == TCM_IFINDEX_MAGIC_BLOCK)
+               return 0;
 
-               /* Find link */
-               dev = dev_get_by_index_rcu(net, ifindex);
-               if (!dev) {
-                       rcu_read_unlock();
-                       return ERR_PTR(-ENODEV);
-               }
+       rcu_read_lock();
 
-               /* Find qdisc */
-               if (!*parent) {
-                       *q = dev->qdisc;
-                       *parent = (*q)->handle;
-               } else {
-                       *q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent));
-                       if (!*q) {
-                               NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists");
-                               err = -EINVAL;
-                               goto errout_rcu;
-                       }
-               }
+       /* Find link */
+       dev = dev_get_by_index_rcu(net, ifindex);
+       if (!dev) {
+               rcu_read_unlock();
+               return -ENODEV;
+       }
 
-               *q = qdisc_refcount_inc_nz(*q);
+       /* Find qdisc */
+       if (!*parent) {
+               *q = dev->qdisc;
+               *parent = (*q)->handle;
+       } else {
+               *q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent));
                if (!*q) {
                        NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists");
                        err = -EINVAL;
                        goto errout_rcu;
                }
+       }
 
-               /* Is it classful? */
-               cops = (*q)->ops->cl_ops;
-               if (!cops) {
-                       NL_SET_ERR_MSG(extack, "Qdisc not classful");
-                       err = -EINVAL;
-                       goto errout_rcu;
-               }
+       *q = qdisc_refcount_inc_nz(*q);
+       if (!*q) {
+               NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists");
+               err = -EINVAL;
+               goto errout_rcu;
+       }
 
-               if (!cops->tcf_block) {
-                       NL_SET_ERR_MSG(extack, "Class doesn't support blocks");
-                       err = -EOPNOTSUPP;
-                       goto errout_rcu;
-               }
+       /* Is it classful? */
+       cops = (*q)->ops->cl_ops;
+       if (!cops) {
+               NL_SET_ERR_MSG(extack, "Qdisc not classful");
+               err = -EINVAL;
+               goto errout_qdisc;
+       }
 
-               /* At this point we know that qdisc is not noop_qdisc,
-                * which means that qdisc holds a reference to net_device
-                * and we hold a reference to qdisc, so it is safe to release
-                * rcu read lock.
-                */
-               rcu_read_unlock();
+       if (!cops->tcf_block) {
+               NL_SET_ERR_MSG(extack, "Class doesn't support blocks");
+               err = -EOPNOTSUPP;
+               goto errout_qdisc;
+       }
 
-               /* Do we search for filter, attached to class? */
-               if (TC_H_MIN(*parent)) {
-                       *cl = cops->find(*q, *parent);
-                       if (*cl == 0) {
-                               NL_SET_ERR_MSG(extack, "Specified class doesn't exist");
-                               err = -ENOENT;
-                               goto errout_qdisc;
-                       }
+errout_rcu:
+       /* At this point we know that qdisc is not noop_qdisc,
+        * which means that qdisc holds a reference to net_device
+        * and we hold a reference to qdisc, so it is safe to release
+        * rcu read lock.
+        */
+       rcu_read_unlock();
+       return err;
+
+errout_qdisc:
+       rcu_read_unlock();
+
+       if (rtnl_held)
+               qdisc_put(*q);
+       else
+               qdisc_put_unlocked(*q);
+       *q = NULL;
+
+       return err;
+}
+
+static int __tcf_qdisc_cl_find(struct Qdisc *q, u32 parent, unsigned long *cl,
+                              int ifindex, struct netlink_ext_ack *extack)
+{
+       if (ifindex == TCM_IFINDEX_MAGIC_BLOCK)
+               return 0;
+
+       /* Do we search for filter, attached to class? */
+       if (TC_H_MIN(parent)) {
+               const struct Qdisc_class_ops *cops = q->ops->cl_ops;
+
+               *cl = cops->find(q, parent);
+               if (*cl == 0) {
+                       NL_SET_ERR_MSG(extack, "Specified class doesn't exist");
+                       return -ENOENT;
                }
+       }
 
-               /* And the last stroke */
-               block = cops->tcf_block(*q, *cl, extack);
+       return 0;
+}
+
+static struct tcf_block *__tcf_block_find(struct net *net, struct Qdisc *q,
+                                         unsigned long cl, int ifindex,
+                                         u32 block_index,
+                                         struct netlink_ext_ack *extack)
+{
+       struct tcf_block *block;
+
+       if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
+               block = tcf_block_refcnt_get(net, block_index);
                if (!block) {
-                       err = -EINVAL;
-                       goto errout_qdisc;
+                       NL_SET_ERR_MSG(extack, "Block of given index was not found");
+                       return ERR_PTR(-EINVAL);
                }
+       } else {
+               const struct Qdisc_class_ops *cops = q->ops->cl_ops;
+
+               block = cops->tcf_block(q, cl, extack);
+               if (!block)
+                       return ERR_PTR(-EINVAL);
+
                if (tcf_block_shared(block)) {
                        NL_SET_ERR_MSG(extack, "This filter block is shared. Please use the block index to manipulate the filters");
-                       err = -EOPNOTSUPP;
-                       goto errout_qdisc;
+                       return ERR_PTR(-EOPNOTSUPP);
                }
 
                /* Always take reference to block in order to support execution
@@ -962,24 +1251,89 @@ static struct tcf_block *tcf_block_find(struct net *net, struct Qdisc **q,
        }
 
        return block;
+}
+
+static void __tcf_block_put(struct tcf_block *block, struct Qdisc *q,
+                           struct tcf_block_ext_info *ei, bool rtnl_held)
+{
+       if (refcount_dec_and_mutex_lock(&block->refcnt, &block->lock)) {
+               /* Flushing/putting all chains will cause the block to be
+                * deallocated when last chain is freed. However, if chain_list
+                * is empty, block has to be manually deallocated. After block
+                * reference counter reached 0, it is no longer possible to
+                * increment it or add new chains to block.
+                */
+               bool free_block = list_empty(&block->chain_list);
+
+               mutex_unlock(&block->lock);
+               if (tcf_block_shared(block))
+                       tcf_block_remove(block, block->net);
+
+               if (q)
+                       tcf_block_offload_unbind(block, q, ei);
+
+               if (free_block)
+                       tcf_block_destroy(block);
+               else
+                       tcf_block_flush_all_chains(block, rtnl_held);
+       } else if (q) {
+               tcf_block_offload_unbind(block, q, ei);
+       }
+}
+
+static void tcf_block_refcnt_put(struct tcf_block *block, bool rtnl_held)
+{
+       __tcf_block_put(block, NULL, NULL, rtnl_held);
+}
+
+/* Find tcf block.
+ * Set q, parent, cl when appropriate.
+ */
+
+static struct tcf_block *tcf_block_find(struct net *net, struct Qdisc **q,
+                                       u32 *parent, unsigned long *cl,
+                                       int ifindex, u32 block_index,
+                                       struct netlink_ext_ack *extack)
+{
+       struct tcf_block *block;
+       int err = 0;
+
+       ASSERT_RTNL();
+
+       err = __tcf_qdisc_find(net, q, parent, ifindex, true, extack);
+       if (err)
+               goto errout;
+
+       err = __tcf_qdisc_cl_find(*q, *parent, cl, ifindex, extack);
+       if (err)
+               goto errout_qdisc;
+
+       block = __tcf_block_find(net, *q, *cl, ifindex, block_index, extack);
+       if (IS_ERR(block))
+               goto errout_qdisc;
+
+       return block;
 
-errout_rcu:
-       rcu_read_unlock();
 errout_qdisc:
-       if (*q) {
+       if (*q)
                qdisc_put(*q);
-               *q = NULL;
-       }
+errout:
+       *q = NULL;
        return ERR_PTR(err);
 }
 
-static void tcf_block_release(struct Qdisc *q, struct tcf_block *block)
+static void tcf_block_release(struct Qdisc *q, struct tcf_block *block,
+                             bool rtnl_held)
 {
        if (!IS_ERR_OR_NULL(block))
-               tcf_block_refcnt_put(block);
+               tcf_block_refcnt_put(block, rtnl_held);
 
-       if (q)
-               qdisc_put(q);
+       if (q) {
+               if (rtnl_held)
+                       qdisc_put(q);
+               else
+                       qdisc_put_unlocked(q);
+       }
 }
 
 struct tcf_block_owner_item {
@@ -1087,7 +1441,7 @@ err_chain0_head_change_cb_add:
        tcf_block_owner_del(block, q, ei->binder_type);
 err_block_owner_add:
 err_block_insert:
-       tcf_block_refcnt_put(block);
+       tcf_block_refcnt_put(block, true);
        return err;
 }
 EXPORT_SYMBOL(tcf_block_get_ext);
@@ -1124,7 +1478,7 @@ void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,
        tcf_chain0_head_change_cb_del(block, ei);
        tcf_block_owner_del(block, q, ei->binder_type);
 
-       __tcf_block_put(block, q, ei);
+       __tcf_block_put(block, q, ei, true);
 }
 EXPORT_SYMBOL(tcf_block_put_ext);
 
@@ -1181,13 +1535,19 @@ tcf_block_playback_offloads(struct tcf_block *block, tc_setup_cb_t *cb,
                            void *cb_priv, bool add, bool offload_in_use,
                            struct netlink_ext_ack *extack)
 {
-       struct tcf_chain *chain;
-       struct tcf_proto *tp;
+       struct tcf_chain *chain, *chain_prev;
+       struct tcf_proto *tp, *tp_prev;
        int err;
 
-       list_for_each_entry(chain, &block->chain_list, list) {
-               for (tp = rtnl_dereference(chain->filter_chain); tp;
-                    tp = rtnl_dereference(tp->next)) {
+       for (chain = __tcf_get_next_chain(block, NULL);
+            chain;
+            chain_prev = chain,
+                    chain = __tcf_get_next_chain(block, chain),
+                    tcf_chain_put(chain_prev)) {
+               for (tp = __tcf_get_next_proto(chain, NULL); tp;
+                    tp_prev = tp,
+                            tp = __tcf_get_next_proto(chain, tp),
+                            tcf_proto_put(tp_prev, true, NULL)) {
                        if (tp->ops->reoffload) {
                                err = tp->ops->reoffload(tp, add, cb, cb_priv,
                                                         extack);
@@ -1204,6 +1564,8 @@ tcf_block_playback_offloads(struct tcf_block *block, tc_setup_cb_t *cb,
        return 0;
 
 err_playback_remove:
+       tcf_proto_put(tp, true, NULL);
+       tcf_chain_put(chain);
        tcf_block_playback_offloads(block, cb, cb_priv, false, offload_in_use,
                                    extack);
        return err;
@@ -1329,32 +1691,116 @@ struct tcf_chain_info {
        struct tcf_proto __rcu *next;
 };
 
-static struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain_info *chain_info)
+static struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain *chain,
+                                          struct tcf_chain_info *chain_info)
 {
-       return rtnl_dereference(*chain_info->pprev);
+       return tcf_chain_dereference(*chain_info->pprev, chain);
 }
 
-static void tcf_chain_tp_insert(struct tcf_chain *chain,
-                               struct tcf_chain_info *chain_info,
-                               struct tcf_proto *tp)
+static int tcf_chain_tp_insert(struct tcf_chain *chain,
+                              struct tcf_chain_info *chain_info,
+                              struct tcf_proto *tp)
 {
+       if (chain->flushing)
+               return -EAGAIN;
+
        if (*chain_info->pprev == chain->filter_chain)
                tcf_chain0_head_change(chain, tp);
-       RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain_info));
+       tcf_proto_get(tp);
+       RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain, chain_info));
        rcu_assign_pointer(*chain_info->pprev, tp);
-       tcf_chain_hold(chain);
+
+       return 0;
 }
 
 static void tcf_chain_tp_remove(struct tcf_chain *chain,
                                struct tcf_chain_info *chain_info,
                                struct tcf_proto *tp)
 {
-       struct tcf_proto *next = rtnl_dereference(chain_info->next);
+       struct tcf_proto *next = tcf_chain_dereference(chain_info->next, chain);
 
+       tcf_proto_mark_delete(tp);
        if (tp == chain->filter_chain)
                tcf_chain0_head_change(chain, next);
        RCU_INIT_POINTER(*chain_info->pprev, next);
-       tcf_chain_put(chain);
+}
+
+static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
+                                          struct tcf_chain_info *chain_info,
+                                          u32 protocol, u32 prio,
+                                          bool prio_allocate);
+
+/* Try to insert new proto.
+ * If proto with specified priority already exists, free new proto
+ * and return existing one.
+ */
+
+static struct tcf_proto *tcf_chain_tp_insert_unique(struct tcf_chain *chain,
+                                                   struct tcf_proto *tp_new,
+                                                   u32 protocol, u32 prio,
+                                                   bool rtnl_held)
+{
+       struct tcf_chain_info chain_info;
+       struct tcf_proto *tp;
+       int err = 0;
+
+       mutex_lock(&chain->filter_chain_lock);
+
+       tp = tcf_chain_tp_find(chain, &chain_info,
+                              protocol, prio, false);
+       if (!tp)
+               err = tcf_chain_tp_insert(chain, &chain_info, tp_new);
+       mutex_unlock(&chain->filter_chain_lock);
+
+       if (tp) {
+               tcf_proto_destroy(tp_new, rtnl_held, NULL);
+               tp_new = tp;
+       } else if (err) {
+               tcf_proto_destroy(tp_new, rtnl_held, NULL);
+               tp_new = ERR_PTR(err);
+       }
+
+       return tp_new;
+}
+
+static void tcf_chain_tp_delete_empty(struct tcf_chain *chain,
+                                     struct tcf_proto *tp, bool rtnl_held,
+                                     struct netlink_ext_ack *extack)
+{
+       struct tcf_chain_info chain_info;
+       struct tcf_proto *tp_iter;
+       struct tcf_proto **pprev;
+       struct tcf_proto *next;
+
+       mutex_lock(&chain->filter_chain_lock);
+
+       /* Atomically find and remove tp from chain. */
+       for (pprev = &chain->filter_chain;
+            (tp_iter = tcf_chain_dereference(*pprev, chain));
+            pprev = &tp_iter->next) {
+               if (tp_iter == tp) {
+                       chain_info.pprev = pprev;
+                       chain_info.next = tp_iter->next;
+                       WARN_ON(tp_iter->deleting);
+                       break;
+               }
+       }
+       /* Verify that tp still exists and no new filters were inserted
+        * concurrently.
+        * Mark tp for deletion if it is empty.
+        */
+       if (!tp_iter || !tcf_proto_check_delete(tp, rtnl_held)) {
+               mutex_unlock(&chain->filter_chain_lock);
+               return;
+       }
+
+       next = tcf_chain_dereference(chain_info.next, chain);
+       if (tp == chain->filter_chain)
+               tcf_chain0_head_change(chain, next);
+       RCU_INIT_POINTER(*chain_info.pprev, next);
+       mutex_unlock(&chain->filter_chain_lock);
+
+       tcf_proto_put(tp, rtnl_held, extack);
 }
 
 static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
@@ -1367,7 +1813,8 @@ static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
 
        /* Check the chain for existence of proto-tcf with this priority */
        for (pprev = &chain->filter_chain;
-            (tp = rtnl_dereference(*pprev)); pprev = &tp->next) {
+            (tp = tcf_chain_dereference(*pprev, chain));
+            pprev = &tp->next) {
                if (tp->prio >= prio) {
                        if (tp->prio == prio) {
                                if (prio_allocate ||
@@ -1380,14 +1827,20 @@ static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
                }
        }
        chain_info->pprev = pprev;
-       chain_info->next = tp ? tp->next : NULL;
+       if (tp) {
+               chain_info->next = tp->next;
+               tcf_proto_get(tp);
+       } else {
+               chain_info->next = NULL;
+       }
        return tp;
 }
 
 static int tcf_fill_node(struct net *net, struct sk_buff *skb,
                         struct tcf_proto *tp, struct tcf_block *block,
                         struct Qdisc *q, u32 parent, void *fh,
-                        u32 portid, u32 seq, u16 flags, int event)
+                        u32 portid, u32 seq, u16 flags, int event,
+                        bool rtnl_held)
 {
        struct tcmsg *tcm;
        struct nlmsghdr  *nlh;
@@ -1415,7 +1868,8 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
        if (!fh) {
                tcm->tcm_handle = 0;
        } else {
-               if (tp->ops->dump && tp->ops->dump(net, tp, fh, skb, tcm) < 0)
+               if (tp->ops->dump &&
+                   tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0)
                        goto nla_put_failure;
        }
        nlh->nlmsg_len = skb_tail_pointer(skb) - b;
@@ -1430,7 +1884,8 @@ nla_put_failure:
 static int tfilter_notify(struct net *net, struct sk_buff *oskb,
                          struct nlmsghdr *n, struct tcf_proto *tp,
                          struct tcf_block *block, struct Qdisc *q,
-                         u32 parent, void *fh, int event, bool unicast)
+                         u32 parent, void *fh, int event, bool unicast,
+                         bool rtnl_held)
 {
        struct sk_buff *skb;
        u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
@@ -1440,7 +1895,8 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
                return -ENOBUFS;
 
        if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
-                         n->nlmsg_seq, n->nlmsg_flags, event) <= 0) {
+                         n->nlmsg_seq, n->nlmsg_flags, event,
+                         rtnl_held) <= 0) {
                kfree_skb(skb);
                return -EINVAL;
        }
@@ -1456,7 +1912,7 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
                              struct nlmsghdr *n, struct tcf_proto *tp,
                              struct tcf_block *block, struct Qdisc *q,
                              u32 parent, void *fh, bool unicast, bool *last,
-                             struct netlink_ext_ack *extack)
+                             bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct sk_buff *skb;
        u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
@@ -1467,13 +1923,14 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
                return -ENOBUFS;
 
        if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
-                         n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER) <= 0) {
+                         n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER,
+                         rtnl_held) <= 0) {
                NL_SET_ERR_MSG(extack, "Failed to build del event notification");
                kfree_skb(skb);
                return -EINVAL;
        }
 
-       err = tp->ops->delete(tp, fh, last, extack);
+       err = tp->ops->delete(tp, fh, last, rtnl_held, extack);
        if (err) {
                kfree_skb(skb);
                return err;
@@ -1492,14 +1949,21 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
 static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
                                 struct tcf_block *block, struct Qdisc *q,
                                 u32 parent, struct nlmsghdr *n,
-                                struct tcf_chain *chain, int event)
+                                struct tcf_chain *chain, int event,
+                                bool rtnl_held)
 {
        struct tcf_proto *tp;
 
-       for (tp = rtnl_dereference(chain->filter_chain);
-            tp; tp = rtnl_dereference(tp->next))
+       for (tp = tcf_get_next_proto(chain, NULL, rtnl_held);
+            tp; tp = tcf_get_next_proto(chain, tp, rtnl_held))
                tfilter_notify(net, oskb, n, tp, block,
-                              q, parent, NULL, event, false);
+                              q, parent, NULL, event, false, rtnl_held);
+}
+
+static void tfilter_put(struct tcf_proto *tp, void *fh)
+{
+       if (tp->ops->put && fh)
+               tp->ops->put(tp, fh);
 }
 
 static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
@@ -1522,6 +1986,7 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
        void *fh;
        int err;
        int tp_created;
+       bool rtnl_held = false;
 
        if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
@@ -1538,7 +2003,9 @@ replay:
        prio = TC_H_MAJ(t->tcm_info);
        prio_allocate = false;
        parent = t->tcm_parent;
+       tp = NULL;
        cl = 0;
+       block = NULL;
 
        if (prio == 0) {
                /* If no priority is provided by the user,
@@ -1555,8 +2022,27 @@ replay:
 
        /* Find head of filter chain. */
 
-       block = tcf_block_find(net, &q, &parent, &cl,
-                              t->tcm_ifindex, t->tcm_block_index, extack);
+       err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
+       if (err)
+               return err;
+
+       /* Take rtnl mutex if rtnl_held was set to true on previous iteration,
+        * block is shared (no qdisc found), qdisc is not unlocked, classifier
+        * type is not specified, classifier is not unlocked.
+        */
+       if (rtnl_held ||
+           (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
+           !tca[TCA_KIND] || !tcf_proto_is_unlocked(nla_data(tca[TCA_KIND]))) {
+               rtnl_held = true;
+               rtnl_lock();
+       }
+
+       err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
+       if (err)
+               goto errout;
+
+       block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
+                                extack);
        if (IS_ERR(block)) {
                err = PTR_ERR(block);
                goto errout;
@@ -1575,40 +2061,62 @@ replay:
                goto errout;
        }
 
+       mutex_lock(&chain->filter_chain_lock);
        tp = tcf_chain_tp_find(chain, &chain_info, protocol,
                               prio, prio_allocate);
        if (IS_ERR(tp)) {
                NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
                err = PTR_ERR(tp);
-               goto errout;
+               goto errout_locked;
        }
 
        if (tp == NULL) {
+               struct tcf_proto *tp_new = NULL;
+
+               if (chain->flushing) {
+                       err = -EAGAIN;
+                       goto errout_locked;
+               }
+
                /* Proto-tcf does not exist, create new one */
 
                if (tca[TCA_KIND] == NULL || !protocol) {
                        NL_SET_ERR_MSG(extack, "Filter kind and protocol must be specified");
                        err = -EINVAL;
-                       goto errout;
+                       goto errout_locked;
                }
 
                if (!(n->nlmsg_flags & NLM_F_CREATE)) {
                        NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter");
                        err = -ENOENT;
-                       goto errout;
+                       goto errout_locked;
                }
 
                if (prio_allocate)
-                       prio = tcf_auto_prio(tcf_chain_tp_prev(&chain_info));
+                       prio = tcf_auto_prio(tcf_chain_tp_prev(chain,
+                                                              &chain_info));
 
-               tp = tcf_proto_create(nla_data(tca[TCA_KIND]),
-                                     protocol, prio, chain, extack);
+               mutex_unlock(&chain->filter_chain_lock);
+               tp_new = tcf_proto_create(nla_data(tca[TCA_KIND]),
+                                         protocol, prio, chain, rtnl_held,
+                                         extack);
+               if (IS_ERR(tp_new)) {
+                       err = PTR_ERR(tp_new);
+                       goto errout_tp;
+               }
+
+               tp_created = 1;
+               tp = tcf_chain_tp_insert_unique(chain, tp_new, protocol, prio,
+                                               rtnl_held);
                if (IS_ERR(tp)) {
                        err = PTR_ERR(tp);
-                       goto errout;
+                       goto errout_tp;
                }
-               tp_created = 1;
-       } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
+       } else {
+               mutex_unlock(&chain->filter_chain_lock);
+       }
+
+       if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
                NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one");
                err = -EINVAL;
                goto errout;
@@ -1623,6 +2131,7 @@ replay:
                        goto errout;
                }
        } else if (n->nlmsg_flags & NLM_F_EXCL) {
+               tfilter_put(tp, fh);
                NL_SET_ERR_MSG(extack, "Filter already exists");
                err = -EEXIST;
                goto errout;
@@ -1636,25 +2145,41 @@ replay:
 
        err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
                              n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE,
-                             extack);
+                             rtnl_held, extack);
        if (err == 0) {
-               if (tp_created)
-                       tcf_chain_tp_insert(chain, &chain_info, tp);
                tfilter_notify(net, skb, n, tp, block, q, parent, fh,
-                              RTM_NEWTFILTER, false);
-       } else {
-               if (tp_created)
-                       tcf_proto_destroy(tp, NULL);
+                              RTM_NEWTFILTER, false, rtnl_held);
+               tfilter_put(tp, fh);
        }
 
 errout:
-       if (chain)
-               tcf_chain_put(chain);
-       tcf_block_release(q, block);
-       if (err == -EAGAIN)
+       if (err && tp_created)
+               tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL);
+errout_tp:
+       if (chain) {
+               if (tp && !IS_ERR(tp))
+                       tcf_proto_put(tp, rtnl_held, NULL);
+               if (!tp_created)
+                       tcf_chain_put(chain);
+       }
+       tcf_block_release(q, block, rtnl_held);
+
+       if (rtnl_held)
+               rtnl_unlock();
+
+       if (err == -EAGAIN) {
+               /* Take rtnl lock in case EAGAIN is caused by concurrent flush
+                * of target chain.
+                */
+               rtnl_held = true;
                /* Replay the request. */
                goto replay;
+       }
        return err;
+
+errout_locked:
+       mutex_unlock(&chain->filter_chain_lock);
+       goto errout;
 }
 
 static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
@@ -1670,11 +2195,12 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
        struct Qdisc *q = NULL;
        struct tcf_chain_info chain_info;
        struct tcf_chain *chain = NULL;
-       struct tcf_block *block;
+       struct tcf_block *block = NULL;
        struct tcf_proto *tp = NULL;
        unsigned long cl = 0;
        void *fh = NULL;
        int err;
+       bool rtnl_held = false;
 
        if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
@@ -1695,8 +2221,27 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 
        /* Find head of filter chain. */
 
-       block = tcf_block_find(net, &q, &parent, &cl,
-                              t->tcm_ifindex, t->tcm_block_index, extack);
+       err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
+       if (err)
+               return err;
+
+       /* Take rtnl mutex if flushing whole chain, block is shared (no qdisc
+        * found), qdisc is not unlocked, classifier type is not specified,
+        * classifier is not unlocked.
+        */
+       if (!prio ||
+           (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
+           !tca[TCA_KIND] || !tcf_proto_is_unlocked(nla_data(tca[TCA_KIND]))) {
+               rtnl_held = true;
+               rtnl_lock();
+       }
+
+       err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
+       if (err)
+               goto errout;
+
+       block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
+                                extack);
        if (IS_ERR(block)) {
                err = PTR_ERR(block);
                goto errout;
@@ -1724,56 +2269,69 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 
        if (prio == 0) {
                tfilter_notify_chain(net, skb, block, q, parent, n,
-                                    chain, RTM_DELTFILTER);
-               tcf_chain_flush(chain);
+                                    chain, RTM_DELTFILTER, rtnl_held);
+               tcf_chain_flush(chain, rtnl_held);
                err = 0;
                goto errout;
        }
 
+       mutex_lock(&chain->filter_chain_lock);
        tp = tcf_chain_tp_find(chain, &chain_info, protocol,
                               prio, false);
        if (!tp || IS_ERR(tp)) {
                NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
                err = tp ? PTR_ERR(tp) : -ENOENT;
-               goto errout;
+               goto errout_locked;
        } else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
                NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one");
                err = -EINVAL;
+               goto errout_locked;
+       } else if (t->tcm_handle == 0) {
+               tcf_chain_tp_remove(chain, &chain_info, tp);
+               mutex_unlock(&chain->filter_chain_lock);
+
+               tcf_proto_put(tp, rtnl_held, NULL);
+               tfilter_notify(net, skb, n, tp, block, q, parent, fh,
+                              RTM_DELTFILTER, false, rtnl_held);
+               err = 0;
                goto errout;
        }
+       mutex_unlock(&chain->filter_chain_lock);
 
        fh = tp->ops->get(tp, t->tcm_handle);
 
        if (!fh) {
-               if (t->tcm_handle == 0) {
-                       tcf_chain_tp_remove(chain, &chain_info, tp);
-                       tfilter_notify(net, skb, n, tp, block, q, parent, fh,
-                                      RTM_DELTFILTER, false);
-                       tcf_proto_destroy(tp, extack);
-                       err = 0;
-               } else {
-                       NL_SET_ERR_MSG(extack, "Specified filter handle not found");
-                       err = -ENOENT;
-               }
+               NL_SET_ERR_MSG(extack, "Specified filter handle not found");
+               err = -ENOENT;
        } else {
                bool last;
 
                err = tfilter_del_notify(net, skb, n, tp, block,
                                         q, parent, fh, false, &last,
-                                        extack);
+                                        rtnl_held, extack);
+
                if (err)
                        goto errout;
-               if (last) {
-                       tcf_chain_tp_remove(chain, &chain_info, tp);
-                       tcf_proto_destroy(tp, extack);
-               }
+               if (last)
+                       tcf_chain_tp_delete_empty(chain, tp, rtnl_held, extack);
        }
 
 errout:
-       if (chain)
+       if (chain) {
+               if (tp && !IS_ERR(tp))
+                       tcf_proto_put(tp, rtnl_held, NULL);
                tcf_chain_put(chain);
-       tcf_block_release(q, block);
+       }
+       tcf_block_release(q, block, rtnl_held);
+
+       if (rtnl_held)
+               rtnl_unlock();
+
        return err;
+
+errout_locked:
+       mutex_unlock(&chain->filter_chain_lock);
+       goto errout;
 }
 
 static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
@@ -1789,11 +2347,12 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
        struct Qdisc *q = NULL;
        struct tcf_chain_info chain_info;
        struct tcf_chain *chain = NULL;
-       struct tcf_block *block;
+       struct tcf_block *block = NULL;
        struct tcf_proto *tp = NULL;
        unsigned long cl = 0;
        void *fh = NULL;
        int err;
+       bool rtnl_held = false;
 
        err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, rtm_tca_policy, extack);
        if (err < 0)
@@ -1811,8 +2370,26 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
 
        /* Find head of filter chain. */
 
-       block = tcf_block_find(net, &q, &parent, &cl,
-                              t->tcm_ifindex, t->tcm_block_index, extack);
+       err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
+       if (err)
+               return err;
+
+       /* Take rtnl mutex if block is shared (no qdisc found), qdisc is not
+        * unlocked, classifier type is not specified, classifier is not
+        * unlocked.
+        */
+       if ((q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
+           !tca[TCA_KIND] || !tcf_proto_is_unlocked(nla_data(tca[TCA_KIND]))) {
+               rtnl_held = true;
+               rtnl_lock();
+       }
+
+       err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
+       if (err)
+               goto errout;
+
+       block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
+                                extack);
        if (IS_ERR(block)) {
                err = PTR_ERR(block);
                goto errout;
@@ -1831,8 +2408,10 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
                goto errout;
        }
 
+       mutex_lock(&chain->filter_chain_lock);
        tp = tcf_chain_tp_find(chain, &chain_info, protocol,
                               prio, false);
+       mutex_unlock(&chain->filter_chain_lock);
        if (!tp || IS_ERR(tp)) {
                NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
                err = tp ? PTR_ERR(tp) : -ENOENT;
@@ -1850,15 +2429,23 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
                err = -ENOENT;
        } else {
                err = tfilter_notify(net, skb, n, tp, block, q, parent,
-                                    fh, RTM_NEWTFILTER, true);
+                                    fh, RTM_NEWTFILTER, true, rtnl_held);
                if (err < 0)
                        NL_SET_ERR_MSG(extack, "Failed to send filter notify message");
        }
 
+       tfilter_put(tp, fh);
 errout:
-       if (chain)
+       if (chain) {
+               if (tp && !IS_ERR(tp))
+                       tcf_proto_put(tp, rtnl_held, NULL);
                tcf_chain_put(chain);
-       tcf_block_release(q, block);
+       }
+       tcf_block_release(q, block, rtnl_held);
+
+       if (rtnl_held)
+               rtnl_unlock();
+
        return err;
 }
 
@@ -1879,7 +2466,7 @@ static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg)
        return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent,
                             n, NETLINK_CB(a->cb->skb).portid,
                             a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                            RTM_NEWTFILTER);
+                            RTM_NEWTFILTER, true);
 }
 
 static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
@@ -1889,11 +2476,15 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
        struct net *net = sock_net(skb->sk);
        struct tcf_block *block = chain->block;
        struct tcmsg *tcm = nlmsg_data(cb->nlh);
+       struct tcf_proto *tp, *tp_prev;
        struct tcf_dump_args arg;
-       struct tcf_proto *tp;
 
-       for (tp = rtnl_dereference(chain->filter_chain);
-            tp; tp = rtnl_dereference(tp->next), (*p_index)++) {
+       for (tp = __tcf_get_next_proto(chain, NULL);
+            tp;
+            tp_prev = tp,
+                    tp = __tcf_get_next_proto(chain, tp),
+                    tcf_proto_put(tp_prev, true, NULL),
+                    (*p_index)++) {
                if (*p_index < index_start)
                        continue;
                if (TC_H_MAJ(tcm->tcm_info) &&
@@ -1909,9 +2500,8 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
                        if (tcf_fill_node(net, skb, tp, block, q, parent, NULL,
                                          NETLINK_CB(cb->skb).portid,
                                          cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                         RTM_NEWTFILTER) <= 0)
-                               return false;
-
+                                         RTM_NEWTFILTER, true) <= 0)
+                               goto errout;
                        cb->args[1] = 1;
                }
                if (!tp->ops->walk)
@@ -1926,23 +2516,27 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
                arg.w.skip = cb->args[1] - 1;
                arg.w.count = 0;
                arg.w.cookie = cb->args[2];
-               tp->ops->walk(tp, &arg.w);
+               tp->ops->walk(tp, &arg.w, true);
                cb->args[2] = arg.w.cookie;
                cb->args[1] = arg.w.count + 1;
                if (arg.w.stop)
-                       return false;
+                       goto errout;
        }
        return true;
+
+errout:
+       tcf_proto_put(tp, true, NULL);
+       return false;
 }
 
 /* called with RTNL */
 static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
 {
+       struct tcf_chain *chain, *chain_prev;
        struct net *net = sock_net(skb->sk);
        struct nlattr *tca[TCA_MAX + 1];
        struct Qdisc *q = NULL;
        struct tcf_block *block;
-       struct tcf_chain *chain;
        struct tcmsg *tcm = nlmsg_data(cb->nlh);
        long index_start;
        long index;
@@ -2006,19 +2600,24 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
        index_start = cb->args[0];
        index = 0;
 
-       list_for_each_entry(chain, &block->chain_list, list) {
+       for (chain = __tcf_get_next_chain(block, NULL);
+            chain;
+            chain_prev = chain,
+                    chain = __tcf_get_next_chain(block, chain),
+                    tcf_chain_put(chain_prev)) {
                if (tca[TCA_CHAIN] &&
                    nla_get_u32(tca[TCA_CHAIN]) != chain->index)
                        continue;
                if (!tcf_chain_dump(chain, q, parent, skb, cb,
                                    index_start, &index)) {
+                       tcf_chain_put(chain);
                        err = -EMSGSIZE;
                        break;
                }
        }
 
        if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK)
-               tcf_block_refcnt_put(block);
+               tcf_block_refcnt_put(block, true);
        cb->args[0] = index;
 
 out:
@@ -2028,8 +2627,10 @@ out:
        return skb->len;
 }
 
-static int tc_chain_fill_node(struct tcf_chain *chain, struct net *net,
-                             struct sk_buff *skb, struct tcf_block *block,
+static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops,
+                             void *tmplt_priv, u32 chain_index,
+                             struct net *net, struct sk_buff *skb,
+                             struct tcf_block *block,
                              u32 portid, u32 seq, u16 flags, int event)
 {
        unsigned char *b = skb_tail_pointer(skb);
@@ -2038,8 +2639,8 @@ static int tc_chain_fill_node(struct tcf_chain *chain, struct net *net,
        struct tcmsg *tcm;
        void *priv;
 
-       ops = chain->tmplt_ops;
-       priv = chain->tmplt_priv;
+       ops = tmplt_ops;
+       priv = tmplt_priv;
 
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
        if (!nlh)
@@ -2057,7 +2658,7 @@ static int tc_chain_fill_node(struct tcf_chain *chain, struct net *net,
                tcm->tcm_block_index = block->index;
        }
 
-       if (nla_put_u32(skb, TCA_CHAIN, chain->index))
+       if (nla_put_u32(skb, TCA_CHAIN, chain_index))
                goto nla_put_failure;
 
        if (ops) {
@@ -2088,7 +2689,8 @@ static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
        if (!skb)
                return -ENOBUFS;
 
-       if (tc_chain_fill_node(chain, net, skb, block, portid,
+       if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
+                              chain->index, net, skb, block, portid,
                               seq, flags, event) <= 0) {
                kfree_skb(skb);
                return -EINVAL;
@@ -2100,6 +2702,31 @@ static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
        return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO);
 }
 
+static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
+                                 void *tmplt_priv, u32 chain_index,
+                                 struct tcf_block *block, struct sk_buff *oskb,
+                                 u32 seq, u16 flags, bool unicast)
+{
+       u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
+       struct net *net = block->net;
+       struct sk_buff *skb;
+
+       skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!skb)
+               return -ENOBUFS;
+
+       if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb,
+                              block, portid, seq, flags, RTM_DELCHAIN) <= 0) {
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       if (unicast)
+               return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
+
+       return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO);
+}
+
 static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net,
                              struct nlattr **tca,
                              struct netlink_ext_ack *extack)
@@ -2111,7 +2738,7 @@ static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net,
        if (!tca[TCA_KIND])
                return 0;
 
-       ops = tcf_proto_lookup_ops(nla_data(tca[TCA_KIND]), extack);
+       ops = tcf_proto_lookup_ops(nla_data(tca[TCA_KIND]), true, extack);
        if (IS_ERR(ops))
                return PTR_ERR(ops);
        if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump) {
@@ -2129,16 +2756,15 @@ static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net,
        return 0;
 }
 
-static void tc_chain_tmplt_del(struct tcf_chain *chain)
+static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops,
+                              void *tmplt_priv)
 {
-       const struct tcf_proto_ops *ops = chain->tmplt_ops;
-
        /* If template ops are set, no work to do for us. */
-       if (!ops)
+       if (!tmplt_ops)
                return;
 
-       ops->tmplt_destroy(chain->tmplt_priv);
-       module_put(ops->owner);
+       tmplt_ops->tmplt_destroy(tmplt_priv);
+       module_put(tmplt_ops->owner);
 }
 
 /* Add/delete/get a chain */
@@ -2181,6 +2807,8 @@ replay:
                err = -EINVAL;
                goto errout_block;
        }
+
+       mutex_lock(&block->lock);
        chain = tcf_chain_lookup(block, chain_index);
        if (n->nlmsg_type == RTM_NEWCHAIN) {
                if (chain) {
@@ -2192,54 +2820,61 @@ replay:
                        } else {
                                NL_SET_ERR_MSG(extack, "Filter chain already exists");
                                err = -EEXIST;
-                               goto errout_block;
+                               goto errout_block_locked;
                        }
                } else {
                        if (!(n->nlmsg_flags & NLM_F_CREATE)) {
                                NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain");
                                err = -ENOENT;
-                               goto errout_block;
+                               goto errout_block_locked;
                        }
                        chain = tcf_chain_create(block, chain_index);
                        if (!chain) {
                                NL_SET_ERR_MSG(extack, "Failed to create filter chain");
                                err = -ENOMEM;
-                               goto errout_block;
+                               goto errout_block_locked;
                        }
                }
        } else {
                if (!chain || tcf_chain_held_by_acts_only(chain)) {
                        NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
                        err = -EINVAL;
-                       goto errout_block;
+                       goto errout_block_locked;
                }
                tcf_chain_hold(chain);
        }
 
+       if (n->nlmsg_type == RTM_NEWCHAIN) {
+               /* Modifying chain requires holding parent block lock. In case
+                * the chain was successfully added, take a reference to the
+                * chain. This ensures that an empty chain does not disappear at
+                * the end of this function.
+                */
+               tcf_chain_hold(chain);
+               chain->explicitly_created = true;
+       }
+       mutex_unlock(&block->lock);
+
        switch (n->nlmsg_type) {
        case RTM_NEWCHAIN:
                err = tc_chain_tmplt_add(chain, net, tca, extack);
-               if (err)
+               if (err) {
+                       tcf_chain_put_explicitly_created(chain);
                        goto errout;
-               /* In case the chain was successfully added, take a reference
-                * to the chain. This ensures that an empty chain
-                * does not disappear at the end of this function.
-                */
-               tcf_chain_hold(chain);
-               chain->explicitly_created = true;
+               }
+
                tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
                                RTM_NEWCHAIN, false);
                break;
        case RTM_DELCHAIN:
                tfilter_notify_chain(net, skb, block, q, parent, n,
-                                    chain, RTM_DELTFILTER);
+                                    chain, RTM_DELTFILTER, true);
                /* Flush the chain first as the user requested chain removal. */
-               tcf_chain_flush(chain);
+               tcf_chain_flush(chain, true);
                /* In case the chain was successfully deleted, put a reference
                 * to the chain previously taken during addition.
                 */
                tcf_chain_put_explicitly_created(chain);
-               chain->explicitly_created = false;
                break;
        case RTM_GETCHAIN:
                err = tc_chain_notify(chain, skb, n->nlmsg_seq,
@@ -2256,21 +2891,25 @@ replay:
 errout:
        tcf_chain_put(chain);
 errout_block:
-       tcf_block_release(q, block);
+       tcf_block_release(q, block, true);
        if (err == -EAGAIN)
                /* Replay the request. */
                goto replay;
        return err;
+
+errout_block_locked:
+       mutex_unlock(&block->lock);
+       goto errout_block;
 }
 
 /* called with RTNL */
 static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
 {
+       struct tcf_chain *chain, *chain_prev;
        struct net *net = sock_net(skb->sk);
        struct nlattr *tca[TCA_MAX + 1];
        struct Qdisc *q = NULL;
        struct tcf_block *block;
-       struct tcf_chain *chain;
        struct tcmsg *tcm = nlmsg_data(cb->nlh);
        long index_start;
        long index;
@@ -2334,7 +2973,11 @@ static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
        index_start = cb->args[0];
        index = 0;
 
-       list_for_each_entry(chain, &block->chain_list, list) {
+       for (chain = __tcf_get_next_chain(block, NULL);
+            chain;
+            chain_prev = chain,
+                    chain = __tcf_get_next_chain(block, chain),
+                    tcf_chain_put(chain_prev)) {
                if ((tca[TCA_CHAIN] &&
                     nla_get_u32(tca[TCA_CHAIN]) != chain->index))
                        continue;
@@ -2342,19 +2985,20 @@ static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
                        index++;
                        continue;
                }
-               if (tcf_chain_held_by_acts_only(chain))
-                       continue;
-               err = tc_chain_fill_node(chain, net, skb, block,
+               err = tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
+                                        chain->index, net, skb, block,
                                         NETLINK_CB(cb->skb).portid,
                                         cb->nlh->nlmsg_seq, NLM_F_MULTI,
                                         RTM_NEWCHAIN);
-               if (err <= 0)
+               if (err <= 0) {
+                       tcf_chain_put(chain);
                        break;
+               }
                index++;
        }
 
        if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK)
-               tcf_block_refcnt_put(block);
+               tcf_block_refcnt_put(block, true);
        cb->args[0] = index;
 
 out:
@@ -2376,7 +3020,7 @@ EXPORT_SYMBOL(tcf_exts_destroy);
 
 int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
                      struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr,
-                     struct netlink_ext_ack *extack)
+                     bool rtnl_held, struct netlink_ext_ack *extack)
 {
 #ifdef CONFIG_NET_CLS_ACT
        {
@@ -2386,7 +3030,8 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
                if (exts->police && tb[exts->police]) {
                        act = tcf_action_init_1(net, tp, tb[exts->police],
                                                rate_tlv, "police", ovr,
-                                               TCA_ACT_BIND, true, extack);
+                                               TCA_ACT_BIND, rtnl_held,
+                                               extack);
                        if (IS_ERR(act))
                                return PTR_ERR(act);
 
@@ -2398,8 +3043,8 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
 
                        err = tcf_action_init(net, tp, tb[exts->action],
                                              rate_tlv, NULL, ovr, TCA_ACT_BIND,
-                                             exts->actions, &attr_size, true,
-                                             extack);
+                                             exts->actions, &attr_size,
+                                             rtnl_held, extack);
                        if (err < 0)
                                return err;
                        exts->nr_actions = err;
@@ -2515,6 +3160,114 @@ int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
 }
 EXPORT_SYMBOL(tc_setup_cb_call);
 
+int tc_setup_flow_action(struct flow_action *flow_action,
+                        const struct tcf_exts *exts)
+{
+       const struct tc_action *act;
+       int i, j, k;
+
+       if (!exts)
+               return 0;
+
+       j = 0;
+       tcf_exts_for_each_action(i, act, exts) {
+               struct flow_action_entry *entry;
+
+               entry = &flow_action->entries[j];
+               if (is_tcf_gact_ok(act)) {
+                       entry->id = FLOW_ACTION_ACCEPT;
+               } else if (is_tcf_gact_shot(act)) {
+                       entry->id = FLOW_ACTION_DROP;
+               } else if (is_tcf_gact_trap(act)) {
+                       entry->id = FLOW_ACTION_TRAP;
+               } else if (is_tcf_gact_goto_chain(act)) {
+                       entry->id = FLOW_ACTION_GOTO;
+                       entry->chain_index = tcf_gact_goto_chain_index(act);
+               } else if (is_tcf_mirred_egress_redirect(act)) {
+                       entry->id = FLOW_ACTION_REDIRECT;
+                       entry->dev = tcf_mirred_dev(act);
+               } else if (is_tcf_mirred_egress_mirror(act)) {
+                       entry->id = FLOW_ACTION_MIRRED;
+                       entry->dev = tcf_mirred_dev(act);
+               } else if (is_tcf_vlan(act)) {
+                       switch (tcf_vlan_action(act)) {
+                       case TCA_VLAN_ACT_PUSH:
+                               entry->id = FLOW_ACTION_VLAN_PUSH;
+                               entry->vlan.vid = tcf_vlan_push_vid(act);
+                               entry->vlan.proto = tcf_vlan_push_proto(act);
+                               entry->vlan.prio = tcf_vlan_push_prio(act);
+                               break;
+                       case TCA_VLAN_ACT_POP:
+                               entry->id = FLOW_ACTION_VLAN_POP;
+                               break;
+                       case TCA_VLAN_ACT_MODIFY:
+                               entry->id = FLOW_ACTION_VLAN_MANGLE;
+                               entry->vlan.vid = tcf_vlan_push_vid(act);
+                               entry->vlan.proto = tcf_vlan_push_proto(act);
+                               entry->vlan.prio = tcf_vlan_push_prio(act);
+                               break;
+                       default:
+                               goto err_out;
+                       }
+               } else if (is_tcf_tunnel_set(act)) {
+                       entry->id = FLOW_ACTION_TUNNEL_ENCAP;
+                       entry->tunnel = tcf_tunnel_info(act);
+               } else if (is_tcf_tunnel_release(act)) {
+                       entry->id = FLOW_ACTION_TUNNEL_DECAP;
+                       entry->tunnel = tcf_tunnel_info(act);
+               } else if (is_tcf_pedit(act)) {
+                       for (k = 0; k < tcf_pedit_nkeys(act); k++) {
+                               switch (tcf_pedit_cmd(act, k)) {
+                               case TCA_PEDIT_KEY_EX_CMD_SET:
+                                       entry->id = FLOW_ACTION_MANGLE;
+                                       break;
+                               case TCA_PEDIT_KEY_EX_CMD_ADD:
+                                       entry->id = FLOW_ACTION_ADD;
+                                       break;
+                               default:
+                                       goto err_out;
+                               }
+                               entry->mangle.htype = tcf_pedit_htype(act, k);
+                               entry->mangle.mask = tcf_pedit_mask(act, k);
+                               entry->mangle.val = tcf_pedit_val(act, k);
+                               entry->mangle.offset = tcf_pedit_offset(act, k);
+                               entry = &flow_action->entries[++j];
+                       }
+               } else if (is_tcf_csum(act)) {
+                       entry->id = FLOW_ACTION_CSUM;
+                       entry->csum_flags = tcf_csum_update_flags(act);
+               } else if (is_tcf_skbedit_mark(act)) {
+                       entry->id = FLOW_ACTION_MARK;
+                       entry->mark = tcf_skbedit_mark(act);
+               } else {
+                       goto err_out;
+               }
+
+               if (!is_tcf_pedit(act))
+                       j++;
+       }
+       return 0;
+err_out:
+       return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(tc_setup_flow_action);
+
+unsigned int tcf_exts_num_actions(struct tcf_exts *exts)
+{
+       unsigned int num_acts = 0;
+       struct tc_action *act;
+       int i;
+
+       tcf_exts_for_each_action(i, act, exts) {
+               if (is_tcf_pedit(act))
+                       num_acts += tcf_pedit_nkeys(act);
+               else
+                       num_acts++;
+       }
+       return num_acts;
+}
+EXPORT_SYMBOL(tcf_exts_num_actions);
+
 static __net_init int tcf_net_init(struct net *net)
 {
        struct tcf_net *tn = net_generic(net, tcf_net_id);
@@ -2555,10 +3308,12 @@ static int __init tc_filter_init(void)
        if (err)
                goto err_rhash_setup_block_ht;
 
-       rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL, 0);
-       rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL, 0);
+       rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL,
+                     RTNL_FLAG_DOIT_UNLOCKED);
+       rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL,
+                     RTNL_FLAG_DOIT_UNLOCKED);
        rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter,
-                     tc_dump_tfilter, 0);
+                     tc_dump_tfilter, RTNL_FLAG_DOIT_UNLOCKED);
        rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0);
        rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0);
        rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain,
index 4a57fec..2383f44 100644 (file)
@@ -107,7 +107,8 @@ static void basic_delete_filter_work(struct work_struct *work)
        rtnl_unlock();
 }
 
-static void basic_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
+static void basic_destroy(struct tcf_proto *tp, bool rtnl_held,
+                         struct netlink_ext_ack *extack)
 {
        struct basic_head *head = rtnl_dereference(tp->root);
        struct basic_filter *f, *n;
@@ -126,7 +127,7 @@ static void basic_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
 }
 
 static int basic_delete(struct tcf_proto *tp, void *arg, bool *last,
-                       struct netlink_ext_ack *extack)
+                       bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct basic_head *head = rtnl_dereference(tp->root);
        struct basic_filter *f = arg;
@@ -153,7 +154,7 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp,
 {
        int err;
 
-       err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, extack);
+       err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, true, extack);
        if (err < 0)
                return err;
 
@@ -173,7 +174,7 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp,
 static int basic_change(struct net *net, struct sk_buff *in_skb,
                        struct tcf_proto *tp, unsigned long base, u32 handle,
                        struct nlattr **tca, void **arg, bool ovr,
-                       struct netlink_ext_ack *extack)
+                       bool rtnl_held, struct netlink_ext_ack *extack)
 {
        int err;
        struct basic_head *head = rtnl_dereference(tp->root);
@@ -247,7 +248,8 @@ errout:
        return err;
 }
 
-static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg,
+                      bool rtnl_held)
 {
        struct basic_head *head = rtnl_dereference(tp->root);
        struct basic_filter *f;
@@ -274,7 +276,7 @@ static void basic_bind_class(void *fh, u32 classid, unsigned long cl)
 }
 
 static int basic_dump(struct net *net, struct tcf_proto *tp, void *fh,
-                     struct sk_buff *skb, struct tcmsg *t)
+                     struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
 {
        struct tc_basic_pcnt gpf = {};
        struct basic_filter *f = fh;
index a95cb24..062350c 100644 (file)
@@ -298,7 +298,7 @@ static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog,
 }
 
 static int cls_bpf_delete(struct tcf_proto *tp, void *arg, bool *last,
-                         struct netlink_ext_ack *extack)
+                         bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct cls_bpf_head *head = rtnl_dereference(tp->root);
 
@@ -307,7 +307,7 @@ static int cls_bpf_delete(struct tcf_proto *tp, void *arg, bool *last,
        return 0;
 }
 
-static void cls_bpf_destroy(struct tcf_proto *tp,
+static void cls_bpf_destroy(struct tcf_proto *tp, bool rtnl_held,
                            struct netlink_ext_ack *extack)
 {
        struct cls_bpf_head *head = rtnl_dereference(tp->root);
@@ -417,7 +417,8 @@ static int cls_bpf_set_parms(struct net *net, struct tcf_proto *tp,
        if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf))
                return -EINVAL;
 
-       ret = tcf_exts_validate(net, tp, tb, est, &prog->exts, ovr, extack);
+       ret = tcf_exts_validate(net, tp, tb, est, &prog->exts, ovr, true,
+                               extack);
        if (ret < 0)
                return ret;
 
@@ -455,7 +456,8 @@ static int cls_bpf_set_parms(struct net *net, struct tcf_proto *tp,
 static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
                          struct tcf_proto *tp, unsigned long base,
                          u32 handle, struct nlattr **tca,
-                         void **arg, bool ovr, struct netlink_ext_ack *extack)
+                         void **arg, bool ovr, bool rtnl_held,
+                         struct netlink_ext_ack *extack)
 {
        struct cls_bpf_head *head = rtnl_dereference(tp->root);
        struct cls_bpf_prog *oldprog = *arg;
@@ -575,7 +577,7 @@ static int cls_bpf_dump_ebpf_info(const struct cls_bpf_prog *prog,
 }
 
 static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, void *fh,
-                       struct sk_buff *skb, struct tcmsg *tm)
+                       struct sk_buff *skb, struct tcmsg *tm, bool rtnl_held)
 {
        struct cls_bpf_prog *prog = fh;
        struct nlattr *nest;
@@ -635,7 +637,8 @@ static void cls_bpf_bind_class(void *fh, u32 classid, unsigned long cl)
                prog->res.class = cl;
 }
 
-static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg,
+                        bool rtnl_held)
 {
        struct cls_bpf_head *head = rtnl_dereference(tp->root);
        struct cls_bpf_prog *prog;
index 3bc01bd..02b0506 100644 (file)
@@ -78,7 +78,7 @@ static void cls_cgroup_destroy_work(struct work_struct *work)
 static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
                             struct tcf_proto *tp, unsigned long base,
                             u32 handle, struct nlattr **tca,
-                            void **arg, bool ovr,
+                            void **arg, bool ovr, bool rtnl_held,
                             struct netlink_ext_ack *extack)
 {
        struct nlattr *tb[TCA_CGROUP_MAX + 1];
@@ -110,7 +110,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
                goto errout;
 
        err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &new->exts, ovr,
-                               extack);
+                               true, extack);
        if (err < 0)
                goto errout;
 
@@ -130,7 +130,7 @@ errout:
        return err;
 }
 
-static void cls_cgroup_destroy(struct tcf_proto *tp,
+static void cls_cgroup_destroy(struct tcf_proto *tp, bool rtnl_held,
                               struct netlink_ext_ack *extack)
 {
        struct cls_cgroup_head *head = rtnl_dereference(tp->root);
@@ -145,18 +145,21 @@ static void cls_cgroup_destroy(struct tcf_proto *tp,
 }
 
 static int cls_cgroup_delete(struct tcf_proto *tp, void *arg, bool *last,
-                            struct netlink_ext_ack *extack)
+                            bool rtnl_held, struct netlink_ext_ack *extack)
 {
        return -EOPNOTSUPP;
 }
 
-static void cls_cgroup_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+static void cls_cgroup_walk(struct tcf_proto *tp, struct tcf_walker *arg,
+                           bool rtnl_held)
 {
        struct cls_cgroup_head *head = rtnl_dereference(tp->root);
 
        if (arg->count < arg->skip)
                goto skip;
 
+       if (!head)
+               return;
        if (arg->fn(tp, head, arg) < 0) {
                arg->stop = 1;
                return;
@@ -166,7 +169,7 @@ skip:
 }
 
 static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, void *fh,
-                          struct sk_buff *skb, struct tcmsg *t)
+                          struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
 {
        struct cls_cgroup_head *head = rtnl_dereference(tp->root);
        struct nlattr *nest;
index 2bb043c..204e2ed 100644 (file)
@@ -391,7 +391,8 @@ static void flow_destroy_filter_work(struct work_struct *work)
 static int flow_change(struct net *net, struct sk_buff *in_skb,
                       struct tcf_proto *tp, unsigned long base,
                       u32 handle, struct nlattr **tca,
-                      void **arg, bool ovr, struct netlink_ext_ack *extack)
+                      void **arg, bool ovr, bool rtnl_held,
+                      struct netlink_ext_ack *extack)
 {
        struct flow_head *head = rtnl_dereference(tp->root);
        struct flow_filter *fold, *fnew;
@@ -445,7 +446,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
                goto err2;
 
        err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &fnew->exts, ovr,
-                               extack);
+                               true, extack);
        if (err < 0)
                goto err2;
 
@@ -566,7 +567,7 @@ err1:
 }
 
 static int flow_delete(struct tcf_proto *tp, void *arg, bool *last,
-                      struct netlink_ext_ack *extack)
+                      bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct flow_head *head = rtnl_dereference(tp->root);
        struct flow_filter *f = arg;
@@ -590,7 +591,8 @@ static int flow_init(struct tcf_proto *tp)
        return 0;
 }
 
-static void flow_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
+static void flow_destroy(struct tcf_proto *tp, bool rtnl_held,
+                        struct netlink_ext_ack *extack)
 {
        struct flow_head *head = rtnl_dereference(tp->root);
        struct flow_filter *f, *next;
@@ -617,7 +619,7 @@ static void *flow_get(struct tcf_proto *tp, u32 handle)
 }
 
 static int flow_dump(struct net *net, struct tcf_proto *tp, void *fh,
-                    struct sk_buff *skb, struct tcmsg *t)
+                    struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
 {
        struct flow_filter *f = fh;
        struct nlattr *nest;
@@ -677,7 +679,8 @@ nla_put_failure:
        return -1;
 }
 
-static void flow_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+static void flow_walk(struct tcf_proto *tp, struct tcf_walker *arg,
+                     bool rtnl_held)
 {
        struct flow_head *head = rtnl_dereference(tp->root);
        struct flow_filter *f;
index f6aa57f..640f83e 100644 (file)
@@ -381,16 +381,31 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
        bool skip_sw = tc_skip_sw(f->flags);
        int err;
 
+       cls_flower.rule = flow_rule_alloc(tcf_exts_num_actions(&f->exts));
+       if (!cls_flower.rule)
+               return -ENOMEM;
+
        tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack);
        cls_flower.command = TC_CLSFLOWER_REPLACE;
        cls_flower.cookie = (unsigned long) f;
-       cls_flower.dissector = &f->mask->dissector;
-       cls_flower.mask = &f->mask->key;
-       cls_flower.key = &f->mkey;
-       cls_flower.exts = &f->exts;
+       cls_flower.rule->match.dissector = &f->mask->dissector;
+       cls_flower.rule->match.mask = &f->mask->key;
+       cls_flower.rule->match.key = &f->mkey;
        cls_flower.classid = f->res.classid;
 
+       err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts);
+       if (err) {
+               kfree(cls_flower.rule);
+               if (skip_sw) {
+                       NL_SET_ERR_MSG_MOD(extack, "Failed to setup flow action");
+                       return err;
+               }
+               return 0;
+       }
+
        err = tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, skip_sw);
+       kfree(cls_flower.rule);
+
        if (err < 0) {
                fl_hw_destroy_filter(tp, f, NULL);
                return err;
@@ -413,10 +428,13 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f)
        tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, NULL);
        cls_flower.command = TC_CLSFLOWER_STATS;
        cls_flower.cookie = (unsigned long) f;
-       cls_flower.exts = &f->exts;
        cls_flower.classid = f->res.classid;
 
        tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
+
+       tcf_exts_stats_update(&f->exts, cls_flower.stats.bytes,
+                             cls_flower.stats.pkts,
+                             cls_flower.stats.lastused);
 }
 
 static bool __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
@@ -451,7 +469,8 @@ static void fl_destroy_sleepable(struct work_struct *work)
        module_put(THIS_MODULE);
 }
 
-static void fl_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
+static void fl_destroy(struct tcf_proto *tp, bool rtnl_held,
+                      struct netlink_ext_ack *extack)
 {
        struct cls_fl_head *head = rtnl_dereference(tp->root);
        struct fl_flow_mask *mask, *next_mask;
@@ -1258,7 +1277,8 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp,
 {
        int err;
 
-       err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, extack);
+       err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, true,
+                               extack);
        if (err < 0)
                return err;
 
@@ -1285,7 +1305,8 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp,
 static int fl_change(struct net *net, struct sk_buff *in_skb,
                     struct tcf_proto *tp, unsigned long base,
                     u32 handle, struct nlattr **tca,
-                    void **arg, bool ovr, struct netlink_ext_ack *extack)
+                    void **arg, bool ovr, bool rtnl_held,
+                    struct netlink_ext_ack *extack)
 {
        struct cls_fl_head *head = rtnl_dereference(tp->root);
        struct cls_fl_filter *fold = *arg;
@@ -1371,7 +1392,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
        if (!tc_skip_hw(fnew->flags)) {
                err = fl_hw_replace_filter(tp, fnew, extack);
                if (err)
-                       goto errout_mask;
+                       goto errout_mask_ht;
        }
 
        if (!tc_in_hw(fnew->flags))
@@ -1401,6 +1422,10 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
        kfree(mask);
        return 0;
 
+errout_mask_ht:
+       rhashtable_remove_fast(&fnew->mask->ht, &fnew->ht_node,
+                              fnew->mask->filter_ht_params);
+
 errout_mask:
        fl_mask_put(head, fnew->mask, false);
 
@@ -1418,7 +1443,7 @@ errout_mask_alloc:
 }
 
 static int fl_delete(struct tcf_proto *tp, void *arg, bool *last,
-                    struct netlink_ext_ack *extack)
+                    bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct cls_fl_head *head = rtnl_dereference(tp->root);
        struct cls_fl_filter *f = arg;
@@ -1430,7 +1455,8 @@ static int fl_delete(struct tcf_proto *tp, void *arg, bool *last,
        return 0;
 }
 
-static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg,
+                   bool rtnl_held)
 {
        struct cls_fl_head *head = rtnl_dereference(tp->root);
        struct cls_fl_filter *f;
@@ -1463,18 +1489,36 @@ static int fl_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb,
                        if (tc_skip_hw(f->flags))
                                continue;
 
+                       cls_flower.rule =
+                               flow_rule_alloc(tcf_exts_num_actions(&f->exts));
+                       if (!cls_flower.rule)
+                               return -ENOMEM;
+
                        tc_cls_common_offload_init(&cls_flower.common, tp,
                                                   f->flags, extack);
                        cls_flower.command = add ?
                                TC_CLSFLOWER_REPLACE : TC_CLSFLOWER_DESTROY;
                        cls_flower.cookie = (unsigned long)f;
-                       cls_flower.dissector = &mask->dissector;
-                       cls_flower.mask = &mask->key;
-                       cls_flower.key = &f->mkey;
-                       cls_flower.exts = &f->exts;
+                       cls_flower.rule->match.dissector = &mask->dissector;
+                       cls_flower.rule->match.mask = &mask->key;
+                       cls_flower.rule->match.key = &f->mkey;
+
+                       err = tc_setup_flow_action(&cls_flower.rule->action,
+                                                  &f->exts);
+                       if (err) {
+                               kfree(cls_flower.rule);
+                               if (tc_skip_sw(f->flags)) {
+                                       NL_SET_ERR_MSG_MOD(extack, "Failed to setup flow action");
+                                       return err;
+                               }
+                               continue;
+                       }
+
                        cls_flower.classid = f->res.classid;
 
                        err = cb(TC_SETUP_CLSFLOWER, &cls_flower, cb_priv);
+                       kfree(cls_flower.rule);
+
                        if (err) {
                                if (add && tc_skip_sw(f->flags))
                                        return err;
@@ -1489,25 +1533,30 @@ static int fl_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb,
        return 0;
 }
 
-static void fl_hw_create_tmplt(struct tcf_chain *chain,
-                              struct fl_flow_tmplt *tmplt)
+static int fl_hw_create_tmplt(struct tcf_chain *chain,
+                             struct fl_flow_tmplt *tmplt)
 {
        struct tc_cls_flower_offload cls_flower = {};
        struct tcf_block *block = chain->block;
-       struct tcf_exts dummy_exts = { 0, };
+
+       cls_flower.rule = flow_rule_alloc(0);
+       if (!cls_flower.rule)
+               return -ENOMEM;
 
        cls_flower.common.chain_index = chain->index;
        cls_flower.command = TC_CLSFLOWER_TMPLT_CREATE;
        cls_flower.cookie = (unsigned long) tmplt;
-       cls_flower.dissector = &tmplt->dissector;
-       cls_flower.mask = &tmplt->mask;
-       cls_flower.key = &tmplt->dummy_key;
-       cls_flower.exts = &dummy_exts;
+       cls_flower.rule->match.dissector = &tmplt->dissector;
+       cls_flower.rule->match.mask = &tmplt->mask;
+       cls_flower.rule->match.key = &tmplt->dummy_key;
 
        /* We don't care if driver (any of them) fails to handle this
         * call. It serves just as a hint for it.
         */
        tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
+       kfree(cls_flower.rule);
+
+       return 0;
 }
 
 static void fl_hw_destroy_tmplt(struct tcf_chain *chain,
@@ -1551,12 +1600,14 @@ static void *fl_tmplt_create(struct net *net, struct tcf_chain *chain,
        err = fl_set_key(net, tb, &tmplt->dummy_key, &tmplt->mask, extack);
        if (err)
                goto errout_tmplt;
-       kfree(tb);
 
        fl_init_dissector(&tmplt->dissector, &tmplt->mask);
 
-       fl_hw_create_tmplt(chain, tmplt);
+       err = fl_hw_create_tmplt(chain, tmplt);
+       if (err)
+               goto errout_tmplt;
 
+       kfree(tb);
        return tmplt;
 
 errout_tmplt:
@@ -2004,7 +2055,7 @@ nla_put_failure:
 }
 
 static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh,
-                  struct sk_buff *skb, struct tcmsg *t)
+                  struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
 {
        struct cls_fl_filter *f = fh;
        struct nlattr *nest;
index 29eeeaf..4e34966 100644 (file)
@@ -139,7 +139,8 @@ static void fw_delete_filter_work(struct work_struct *work)
        rtnl_unlock();
 }
 
-static void fw_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
+static void fw_destroy(struct tcf_proto *tp, bool rtnl_held,
+                      struct netlink_ext_ack *extack)
 {
        struct fw_head *head = rtnl_dereference(tp->root);
        struct fw_filter *f;
@@ -163,7 +164,7 @@ static void fw_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
 }
 
 static int fw_delete(struct tcf_proto *tp, void *arg, bool *last,
-                    struct netlink_ext_ack *extack)
+                    bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct fw_head *head = rtnl_dereference(tp->root);
        struct fw_filter *f = arg;
@@ -217,7 +218,7 @@ static int fw_set_parms(struct net *net, struct tcf_proto *tp,
        int err;
 
        err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &f->exts, ovr,
-                               extack);
+                               true, extack);
        if (err < 0)
                return err;
 
@@ -250,7 +251,8 @@ static int fw_set_parms(struct net *net, struct tcf_proto *tp,
 static int fw_change(struct net *net, struct sk_buff *in_skb,
                     struct tcf_proto *tp, unsigned long base,
                     u32 handle, struct nlattr **tca, void **arg,
-                    bool ovr, struct netlink_ext_ack *extack)
+                    bool ovr, bool rtnl_held,
+                    struct netlink_ext_ack *extack)
 {
        struct fw_head *head = rtnl_dereference(tp->root);
        struct fw_filter *f = *arg;
@@ -354,15 +356,13 @@ errout:
        return err;
 }
 
-static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg,
+                   bool rtnl_held)
 {
        struct fw_head *head = rtnl_dereference(tp->root);
        int h;
 
-       if (head == NULL)
-               arg->stop = 1;
-
-       if (arg->stop)
+       if (head == NULL || arg->stop)
                return;
 
        for (h = 0; h < HTSIZE; h++) {
@@ -384,7 +384,7 @@ static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
 }
 
 static int fw_dump(struct net *net, struct tcf_proto *tp, void *fh,
-                  struct sk_buff *skb, struct tcmsg *t)
+                  struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
 {
        struct fw_head *head = rtnl_dereference(tp->root);
        struct fw_filter *f = fh;
index a1b803f..1f9d481 100644 (file)
@@ -109,7 +109,8 @@ static int mall_replace_hw_filter(struct tcf_proto *tp,
        return 0;
 }
 
-static void mall_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
+static void mall_destroy(struct tcf_proto *tp, bool rtnl_held,
+                        struct netlink_ext_ack *extack)
 {
        struct cls_mall_head *head = rtnl_dereference(tp->root);
 
@@ -145,7 +146,8 @@ static int mall_set_parms(struct net *net, struct tcf_proto *tp,
 {
        int err;
 
-       err = tcf_exts_validate(net, tp, tb, est, &head->exts, ovr, extack);
+       err = tcf_exts_validate(net, tp, tb, est, &head->exts, ovr, true,
+                               extack);
        if (err < 0)
                return err;
 
@@ -159,7 +161,8 @@ static int mall_set_parms(struct net *net, struct tcf_proto *tp,
 static int mall_change(struct net *net, struct sk_buff *in_skb,
                       struct tcf_proto *tp, unsigned long base,
                       u32 handle, struct nlattr **tca,
-                      void **arg, bool ovr, struct netlink_ext_ack *extack)
+                      void **arg, bool ovr, bool rtnl_held,
+                      struct netlink_ext_ack *extack)
 {
        struct cls_mall_head *head = rtnl_dereference(tp->root);
        struct nlattr *tb[TCA_MATCHALL_MAX + 1];
@@ -232,17 +235,21 @@ err_exts_init:
 }
 
 static int mall_delete(struct tcf_proto *tp, void *arg, bool *last,
-                      struct netlink_ext_ack *extack)
+                      bool rtnl_held, struct netlink_ext_ack *extack)
 {
        return -EOPNOTSUPP;
 }
 
-static void mall_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+static void mall_walk(struct tcf_proto *tp, struct tcf_walker *arg,
+                     bool rtnl_held)
 {
        struct cls_mall_head *head = rtnl_dereference(tp->root);
 
        if (arg->count < arg->skip)
                goto skip;
+
+       if (!head)
+               return;
        if (arg->fn(tp, head, arg) < 0)
                arg->stop = 1;
 skip:
@@ -279,7 +286,7 @@ static int mall_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb,
 }
 
 static int mall_dump(struct net *net, struct tcf_proto *tp, void *fh,
-                    struct sk_buff *skb, struct tcmsg *t)
+                    struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
 {
        struct tc_matchall_pcnt gpf = {};
        struct cls_mall_head *head = fh;
index 0404aa5..444d15a 100644 (file)
@@ -276,7 +276,8 @@ static void route4_queue_work(struct route4_filter *f)
        tcf_queue_work(&f->rwork, route4_delete_filter_work);
 }
 
-static void route4_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
+static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
+                          struct netlink_ext_ack *extack)
 {
        struct route4_head *head = rtnl_dereference(tp->root);
        int h1, h2;
@@ -312,7 +313,7 @@ static void route4_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
 }
 
 static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,
-                        struct netlink_ext_ack *extack)
+                        bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct route4_head *head = rtnl_dereference(tp->root);
        struct route4_filter *f = arg;
@@ -393,7 +394,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
        struct route4_bucket *b;
        int err;
 
-       err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, extack);
+       err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, true, extack);
        if (err < 0)
                return err;
 
@@ -468,7 +469,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
 static int route4_change(struct net *net, struct sk_buff *in_skb,
                         struct tcf_proto *tp, unsigned long base, u32 handle,
                         struct nlattr **tca, void **arg, bool ovr,
-                        struct netlink_ext_ack *extack)
+                        bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct route4_head *head = rtnl_dereference(tp->root);
        struct route4_filter __rcu **fp;
@@ -560,15 +561,13 @@ errout:
        return err;
 }
 
-static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg,
+                       bool rtnl_held)
 {
        struct route4_head *head = rtnl_dereference(tp->root);
        unsigned int h, h1;
 
-       if (head == NULL)
-               arg->stop = 1;
-
-       if (arg->stop)
+       if (head == NULL || arg->stop)
                return;
 
        for (h = 0; h <= 256; h++) {
@@ -597,7 +596,7 @@ static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg)
 }
 
 static int route4_dump(struct net *net, struct tcf_proto *tp, void *fh,
-                      struct sk_buff *skb, struct tcmsg *t)
+                      struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
 {
        struct route4_filter *f = fh;
        struct nlattr *nest;
index e9ccf7d..4d38361 100644 (file)
@@ -312,7 +312,8 @@ static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
                __rsvp_delete_filter(f);
 }
 
-static void rsvp_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
+static void rsvp_destroy(struct tcf_proto *tp, bool rtnl_held,
+                        struct netlink_ext_ack *extack)
 {
        struct rsvp_head *data = rtnl_dereference(tp->root);
        int h1, h2;
@@ -341,7 +342,7 @@ static void rsvp_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
 }
 
 static int rsvp_delete(struct tcf_proto *tp, void *arg, bool *last,
-                      struct netlink_ext_ack *extack)
+                      bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct rsvp_head *head = rtnl_dereference(tp->root);
        struct rsvp_filter *nfp, *f = arg;
@@ -477,7 +478,8 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
                       struct tcf_proto *tp, unsigned long base,
                       u32 handle,
                       struct nlattr **tca,
-                      void **arg, bool ovr, struct netlink_ext_ack *extack)
+                      void **arg, bool ovr, bool rtnl_held,
+                      struct netlink_ext_ack *extack)
 {
        struct rsvp_head *data = rtnl_dereference(tp->root);
        struct rsvp_filter *f, *nfp;
@@ -502,7 +504,8 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
        err = tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
        if (err < 0)
                return err;
-       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr, extack);
+       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr, true,
+                               extack);
        if (err < 0)
                goto errout2;
 
@@ -654,7 +657,8 @@ errout2:
        return err;
 }
 
-static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg,
+                     bool rtnl_held)
 {
        struct rsvp_head *head = rtnl_dereference(tp->root);
        unsigned int h, h1;
@@ -688,7 +692,7 @@ static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg)
 }
 
 static int rsvp_dump(struct net *net, struct tcf_proto *tp, void *fh,
-                    struct sk_buff *skb, struct tcmsg *t)
+                    struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
 {
        struct rsvp_filter *f = fh;
        struct rsvp_session *s;
index 9ccc93f..e198162 100644 (file)
@@ -173,7 +173,7 @@ static void tcindex_destroy_fexts_work(struct work_struct *work)
 }
 
 static int tcindex_delete(struct tcf_proto *tp, void *arg, bool *last,
-                         struct netlink_ext_ack *extack)
+                         bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct tcindex_data *p = rtnl_dereference(tp->root);
        struct tcindex_filter_result *r = arg;
@@ -226,7 +226,7 @@ static int tcindex_destroy_element(struct tcf_proto *tp,
 {
        bool last;
 
-       return tcindex_delete(tp, arg, &last, NULL);
+       return tcindex_delete(tp, arg, &last, false, NULL);
 }
 
 static void __tcindex_destroy(struct rcu_head *head)
@@ -275,7 +275,7 @@ static void tcindex_free_perfect_hash(struct tcindex_data *cp)
        kfree(cp->perfect);
 }
 
-static int tcindex_alloc_perfect_hash(struct tcindex_data *cp)
+static int tcindex_alloc_perfect_hash(struct net *net, struct tcindex_data *cp)
 {
        int i, err = 0;
 
@@ -289,6 +289,9 @@ static int tcindex_alloc_perfect_hash(struct tcindex_data *cp)
                                    TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
                if (err < 0)
                        goto errout;
+#ifdef CONFIG_NET_CLS_ACT
+               cp->perfect[i].exts.net = net;
+#endif
        }
 
        return 0;
@@ -305,16 +308,16 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
                  struct nlattr *est, bool ovr, struct netlink_ext_ack *extack)
 {
        struct tcindex_filter_result new_filter_result, *old_r = r;
-       struct tcindex_filter_result cr;
        struct tcindex_data *cp = NULL, *oldp;
        struct tcindex_filter *f = NULL; /* make gcc behave */
+       struct tcf_result cr = {};
        int err, balloc = 0;
        struct tcf_exts e;
 
        err = tcf_exts_init(&e, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
        if (err < 0)
                return err;
-       err = tcf_exts_validate(net, tp, tb, est, &e, ovr, extack);
+       err = tcf_exts_validate(net, tp, tb, est, &e, ovr, true, extack);
        if (err < 0)
                goto errout;
 
@@ -337,7 +340,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
        if (p->perfect) {
                int i;
 
-               if (tcindex_alloc_perfect_hash(cp) < 0)
+               if (tcindex_alloc_perfect_hash(net, cp) < 0)
                        goto errout;
                for (i = 0; i < cp->hash; i++)
                        cp->perfect[i].res = p->perfect[i].res;
@@ -348,11 +351,8 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
        err = tcindex_filter_result_init(&new_filter_result);
        if (err < 0)
                goto errout1;
-       err = tcindex_filter_result_init(&cr);
-       if (err < 0)
-               goto errout1;
        if (old_r)
-               cr.res = r->res;
+               cr = r->res;
 
        if (tb[TCA_TCINDEX_HASH])
                cp->hash = nla_get_u32(tb[TCA_TCINDEX_HASH]);
@@ -406,7 +406,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
        err = -ENOMEM;
        if (!cp->perfect && !cp->h) {
                if (valid_perfect_hash(cp)) {
-                       if (tcindex_alloc_perfect_hash(cp) < 0)
+                       if (tcindex_alloc_perfect_hash(net, cp) < 0)
                                goto errout_alloc;
                        balloc = 1;
                } else {
@@ -443,8 +443,8 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
        }
 
        if (tb[TCA_TCINDEX_CLASSID]) {
-               cr.res.classid = nla_get_u32(tb[TCA_TCINDEX_CLASSID]);
-               tcf_bind_filter(tp, &cr.res, base);
+               cr.classid = nla_get_u32(tb[TCA_TCINDEX_CLASSID]);
+               tcf_bind_filter(tp, &cr, base);
        }
 
        if (old_r && old_r != r) {
@@ -456,7 +456,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
        }
 
        oldp = p;
-       r->res = cr.res;
+       r->res = cr;
        tcf_exts_change(&r->exts, &e);
 
        rcu_assign_pointer(tp->root, cp);
@@ -475,6 +475,8 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
                                ; /* nothing */
 
                rcu_assign_pointer(*fp, f);
+       } else {
+               tcf_exts_destroy(&new_filter_result.exts);
        }
 
        if (oldp)
@@ -487,7 +489,6 @@ errout_alloc:
        else if (balloc == 2)
                kfree(cp->h);
 errout1:
-       tcf_exts_destroy(&cr.exts);
        tcf_exts_destroy(&new_filter_result.exts);
 errout:
        kfree(cp);
@@ -499,7 +500,7 @@ static int
 tcindex_change(struct net *net, struct sk_buff *in_skb,
               struct tcf_proto *tp, unsigned long base, u32 handle,
               struct nlattr **tca, void **arg, bool ovr,
-              struct netlink_ext_ack *extack)
+              bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct nlattr *opt = tca[TCA_OPTIONS];
        struct nlattr *tb[TCA_TCINDEX_MAX + 1];
@@ -522,7 +523,8 @@ tcindex_change(struct net *net, struct sk_buff *in_skb,
                                 tca[TCA_RATE], ovr, extack);
 }
 
-static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker)
+static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker,
+                        bool rtnl_held)
 {
        struct tcindex_data *p = rtnl_dereference(tp->root);
        struct tcindex_filter *f, *next;
@@ -558,7 +560,7 @@ static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker)
        }
 }
 
-static void tcindex_destroy(struct tcf_proto *tp,
+static void tcindex_destroy(struct tcf_proto *tp, bool rtnl_held,
                            struct netlink_ext_ack *extack)
 {
        struct tcindex_data *p = rtnl_dereference(tp->root);
@@ -568,14 +570,14 @@ static void tcindex_destroy(struct tcf_proto *tp,
        walker.count = 0;
        walker.skip = 0;
        walker.fn = tcindex_destroy_element;
-       tcindex_walk(tp, &walker);
+       tcindex_walk(tp, &walker, true);
 
        call_rcu(&p->rcu, __tcindex_destroy);
 }
 
 
 static int tcindex_dump(struct net *net, struct tcf_proto *tp, void *fh,
-                       struct sk_buff *skb, struct tcmsg *t)
+                       struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
 {
        struct tcindex_data *p = rtnl_dereference(tp->root);
        struct tcindex_filter_result *r = fh;
index dcea210..27d29c0 100644 (file)
@@ -629,7 +629,8 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
        return -ENOENT;
 }
 
-static void u32_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
+static void u32_destroy(struct tcf_proto *tp, bool rtnl_held,
+                       struct netlink_ext_ack *extack)
 {
        struct tc_u_common *tp_c = tp->data;
        struct tc_u_hnode *root_ht = rtnl_dereference(tp->root);
@@ -663,7 +664,7 @@ static void u32_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
 }
 
 static int u32_delete(struct tcf_proto *tp, void *arg, bool *last,
-                     struct netlink_ext_ack *extack)
+                     bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct tc_u_hnode *ht = arg;
        struct tc_u_common *tp_c = tp->data;
@@ -726,7 +727,7 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp,
 {
        int err;
 
-       err = tcf_exts_validate(net, tp, tb, est, &n->exts, ovr, extack);
+       err = tcf_exts_validate(net, tp, tb, est, &n->exts, ovr, true, extack);
        if (err < 0)
                return err;
 
@@ -858,7 +859,7 @@ static struct tc_u_knode *u32_init_knode(struct tcf_proto *tp,
 
 static int u32_change(struct net *net, struct sk_buff *in_skb,
                      struct tcf_proto *tp, unsigned long base, u32 handle,
-                     struct nlattr **tca, void **arg, bool ovr,
+                     struct nlattr **tca, void **arg, bool ovr, bool rtnl_held,
                      struct netlink_ext_ack *extack)
 {
        struct tc_u_common *tp_c = tp->data;
@@ -1123,7 +1124,8 @@ erridr:
        return err;
 }
 
-static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg,
+                    bool rtnl_held)
 {
        struct tc_u_common *tp_c = tp->data;
        struct tc_u_hnode *ht;
@@ -1281,7 +1283,7 @@ static void u32_bind_class(void *fh, u32 classid, unsigned long cl)
 }
 
 static int u32_dump(struct net *net, struct tcf_proto *tp, void *fh,
-                   struct sk_buff *skb, struct tcmsg *t)
+                   struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
 {
        struct tc_u_knode *n = fh;
        struct tc_u_hnode *ht_up, *ht_down;
index 03e26e8..b8a388e 100644 (file)
@@ -1201,9 +1201,11 @@ static struct Qdisc *qdisc_create(struct net_device *dev,
        } else {
                if (handle == 0) {
                        handle = qdisc_alloc_handle(dev);
-                       err = -ENOMEM;
-                       if (handle == 0)
+                       if (handle == 0) {
+                               NL_SET_ERR_MSG(extack, "Maximum number of qdisc handles was exceeded");
+                               err = -ENOSPC;
                                goto err_out3;
+                       }
                }
                if (!netif_is_multiqueue(dev))
                        sch->flags |= TCQ_F_ONETXQUEUE;
@@ -1909,17 +1911,19 @@ static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid,
        block = cops->tcf_block(q, cl, NULL);
        if (!block)
                return;
-       list_for_each_entry(chain, &block->chain_list, list) {
+       for (chain = tcf_get_next_chain(block, NULL);
+            chain;
+            chain = tcf_get_next_chain(block, chain)) {
                struct tcf_proto *tp;
 
-               for (tp = rtnl_dereference(chain->filter_chain);
-                    tp; tp = rtnl_dereference(tp->next)) {
+               for (tp = tcf_get_next_proto(chain, NULL, true);
+                    tp; tp = tcf_get_next_proto(chain, tp, true)) {
                        struct tcf_bind_args arg = {};
 
                        arg.w.fn = tcf_node_bind;
                        arg.classid = clid;
                        arg.cl = new_cl;
-                       tp->ops->walk(tp, &arg.w);
+                       tp->ops->walk(tp, &arg.w, true);
                }
        }
 }
index 66ba2ce..38e5add 100644 (file)
@@ -500,7 +500,7 @@ static void dev_watchdog_down(struct net_device *dev)
  *     netif_carrier_on - set carrier
  *     @dev: network device
  *
- * Device has detected that carrier.
+ * Device has detected acquisition of carrier.
  */
 void netif_carrier_on(struct net_device *dev)
 {
@@ -1366,7 +1366,11 @@ static void mini_qdisc_rcu_func(struct rcu_head *head)
 void mini_qdisc_pair_swap(struct mini_Qdisc_pair *miniqp,
                          struct tcf_proto *tp_head)
 {
-       struct mini_Qdisc *miniq_old = rtnl_dereference(*miniqp->p_miniq);
+       /* Protected with chain0->filter_chain_lock.
+        * Can't access chain directly because tp_head can be NULL.
+        */
+       struct mini_Qdisc *miniq_old =
+               rcu_dereference_protected(*miniqp->p_miniq, 1);
        struct mini_Qdisc *miniq;
 
        if (!tp_head) {
index 201c888..d2c7d0d 100644 (file)
@@ -101,7 +101,7 @@ static struct sctp_association *sctp_association_init(
         * socket values.
         */
        asoc->max_retrans = sp->assocparams.sasoc_asocmaxrxt;
-       asoc->pf_retrans  = net->sctp.pf_retrans;
+       asoc->pf_retrans  = sp->pf_retrans;
 
        asoc->rto_initial = msecs_to_jiffies(sp->rtoinfo.srto_initial);
        asoc->rto_max = msecs_to_jiffies(sp->rtoinfo.srto_max);
@@ -1651,8 +1651,11 @@ int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp)
        if (preload)
                idr_preload(gfp);
        spin_lock_bh(&sctp_assocs_id_lock);
-       /* 0 is not a valid assoc_id, must be >= 1 */
-       ret = idr_alloc_cyclic(&sctp_assocs_id, asoc, 1, 0, GFP_NOWAIT);
+       /* 0, 1, 2 are used as SCTP_FUTURE_ASSOC, SCTP_CURRENT_ASSOC and
+        * SCTP_ALL_ASSOC, so an available id must be > SCTP_ALL_ASSOC.
+        */
+       ret = idr_alloc_cyclic(&sctp_assocs_id, asoc, SCTP_ALL_ASSOC + 1, 0,
+                              GFP_NOWAIT);
        spin_unlock_bh(&sctp_assocs_id_lock);
        if (preload)
                idr_preload_end();
index 078f01a..435847d 100644 (file)
@@ -256,6 +256,7 @@ static size_t inet_assoc_attr_size(struct sctp_association *asoc)
                + nla_total_size(1) /* INET_DIAG_TOS */
                + nla_total_size(1) /* INET_DIAG_TCLASS */
                + nla_total_size(4) /* INET_DIAG_MARK */
+               + nla_total_size(4) /* INET_DIAG_CLASS_ID */
                + nla_total_size(addrlen * asoc->peer.transport_count)
                + nla_total_size(addrlen * addrcnt)
                + nla_total_size(sizeof(struct inet_diag_meminfo))
index 123e9f2..edfcf16 100644 (file)
@@ -36,6 +36,7 @@ static __le32 sctp_gso_make_checksum(struct sk_buff *skb)
 {
        skb->ip_summed = CHECKSUM_NONE;
        skb->csum_not_inet = 0;
+       gso_reset_checksum(skb, ~0);
        return sctp_compute_cksum(skb, skb_transport_offset(skb));
 }
 
index c37e1c2..fd33281 100644 (file)
@@ -212,7 +212,7 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
        INIT_LIST_HEAD(&q->retransmit);
        INIT_LIST_HEAD(&q->sacked);
        INIT_LIST_HEAD(&q->abandoned);
-       sctp_sched_set_sched(asoc, SCTP_SS_DEFAULT);
+       sctp_sched_set_sched(asoc, sctp_sk(asoc->base.sk)->default_ss);
 }
 
 /* Free the outqueue structure and any related pending chunks.
index f93c3cf..a78e55a 100644 (file)
@@ -248,7 +248,7 @@ struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
        }
 
        /* Otherwise this is a UDP-style socket. */
-       if (!id || (id == (sctp_assoc_t)-1))
+       if (id <= SCTP_ALL_ASSOC)
                return NULL;
 
        spin_lock_bh(&sctp_assocs_id_lock);
@@ -2027,7 +2027,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
        struct sctp_transport *transport = NULL;
        struct sctp_sndrcvinfo _sinfo, *sinfo;
-       struct sctp_association *asoc;
+       struct sctp_association *asoc, *tmp;
        struct sctp_cmsgs cmsgs;
        union sctp_addr *daddr;
        bool new = false;
@@ -2053,7 +2053,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
 
        /* SCTP_SENDALL process */
        if ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP)) {
-               list_for_each_entry(asoc, &ep->asocs, asocs) {
+               list_for_each_entry_safe(asoc, tmp, &ep->asocs, asocs) {
                        err = sctp_sendmsg_check_sflags(asoc, sflags, msg,
                                                        msg_len);
                        if (err == 0)
@@ -2750,12 +2750,13 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk,
                        return -EINVAL;
        }
 
-       /* Get association, if assoc_id != 0 and the socket is a one
-        * to many style socket, and an association was not found, then
-        * the id was invalid.
+       /* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the
+        * socket is a one to many style socket, and an association
+        * was not found, then the id was invalid.
         */
        asoc = sctp_id2assoc(sk, params.spp_assoc_id);
-       if (!asoc && params.spp_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && params.spp_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
        /* Heartbeat demand can only be sent on a transport or
@@ -2797,6 +2798,43 @@ static inline __u32 sctp_spp_sackdelay_disable(__u32 param_flags)
        return (param_flags & ~SPP_SACKDELAY) | SPP_SACKDELAY_DISABLE;
 }
 
+static void sctp_apply_asoc_delayed_ack(struct sctp_sack_info *params,
+                                       struct sctp_association *asoc)
+{
+       struct sctp_transport *trans;
+
+       if (params->sack_delay) {
+               asoc->sackdelay = msecs_to_jiffies(params->sack_delay);
+               asoc->param_flags =
+                       sctp_spp_sackdelay_enable(asoc->param_flags);
+       }
+       if (params->sack_freq == 1) {
+               asoc->param_flags =
+                       sctp_spp_sackdelay_disable(asoc->param_flags);
+       } else if (params->sack_freq > 1) {
+               asoc->sackfreq = params->sack_freq;
+               asoc->param_flags =
+                       sctp_spp_sackdelay_enable(asoc->param_flags);
+       }
+
+       list_for_each_entry(trans, &asoc->peer.transport_addr_list,
+                           transports) {
+               if (params->sack_delay) {
+                       trans->sackdelay = msecs_to_jiffies(params->sack_delay);
+                       trans->param_flags =
+                               sctp_spp_sackdelay_enable(trans->param_flags);
+               }
+               if (params->sack_freq == 1) {
+                       trans->param_flags =
+                               sctp_spp_sackdelay_disable(trans->param_flags);
+               } else if (params->sack_freq > 1) {
+                       trans->sackfreq = params->sack_freq;
+                       trans->param_flags =
+                               sctp_spp_sackdelay_enable(trans->param_flags);
+               }
+       }
+}
+
 /*
  * 7.1.23.  Get or set delayed ack timer (SCTP_DELAYED_SACK)
  *
@@ -2836,10 +2874,9 @@ static inline __u32 sctp_spp_sackdelay_disable(__u32 param_flags)
 static int sctp_setsockopt_delayed_ack(struct sock *sk,
                                       char __user *optval, unsigned int optlen)
 {
-       struct sctp_sack_info    params;
-       struct sctp_transport   *trans = NULL;
-       struct sctp_association *asoc = NULL;
-       struct sctp_sock        *sp = sctp_sk(sk);
+       struct sctp_sock *sp = sctp_sk(sk);
+       struct sctp_association *asoc;
+       struct sctp_sack_info params;
 
        if (optlen == sizeof(struct sctp_sack_info)) {
                if (copy_from_user(&params, optval, optlen))
@@ -2867,67 +2904,42 @@ static int sctp_setsockopt_delayed_ack(struct sock *sk,
        if (params.sack_delay > 500)
                return -EINVAL;
 
-       /* Get association, if sack_assoc_id != 0 and the socket is a one
-        * to many style socket, and an association was not found, then
-        * the id was invalid.
+       /* Get association, if sack_assoc_id != SCTP_FUTURE_ASSOC and the
+        * socket is a one to many style socket, and an association
+        * was not found, then the id was invalid.
         */
        asoc = sctp_id2assoc(sk, params.sack_assoc_id);
-       if (!asoc && params.sack_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && params.sack_assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
-       if (params.sack_delay) {
-               if (asoc) {
-                       asoc->sackdelay =
-                               msecs_to_jiffies(params.sack_delay);
-                       asoc->param_flags =
-                               sctp_spp_sackdelay_enable(asoc->param_flags);
-               } else {
+       if (asoc) {
+               sctp_apply_asoc_delayed_ack(&params, asoc);
+
+               return 0;
+       }
+
+       if (params.sack_assoc_id == SCTP_FUTURE_ASSOC ||
+           params.sack_assoc_id == SCTP_ALL_ASSOC) {
+               if (params.sack_delay) {
                        sp->sackdelay = params.sack_delay;
                        sp->param_flags =
                                sctp_spp_sackdelay_enable(sp->param_flags);
                }
-       }
-
-       if (params.sack_freq == 1) {
-               if (asoc) {
-                       asoc->param_flags =
-                               sctp_spp_sackdelay_disable(asoc->param_flags);
-               } else {
+               if (params.sack_freq == 1) {
                        sp->param_flags =
                                sctp_spp_sackdelay_disable(sp->param_flags);
-               }
-       } else if (params.sack_freq > 1) {
-               if (asoc) {
-                       asoc->sackfreq = params.sack_freq;
-                       asoc->param_flags =
-                               sctp_spp_sackdelay_enable(asoc->param_flags);
-               } else {
+               } else if (params.sack_freq > 1) {
                        sp->sackfreq = params.sack_freq;
                        sp->param_flags =
                                sctp_spp_sackdelay_enable(sp->param_flags);
                }
        }
 
-       /* If change is for association, also apply to each transport. */
-       if (asoc) {
-               list_for_each_entry(trans, &asoc->peer.transport_addr_list,
-                               transports) {
-                       if (params.sack_delay) {
-                               trans->sackdelay =
-                                       msecs_to_jiffies(params.sack_delay);
-                               trans->param_flags =
-                                       sctp_spp_sackdelay_enable(trans->param_flags);
-                       }
-                       if (params.sack_freq == 1) {
-                               trans->param_flags =
-                                       sctp_spp_sackdelay_disable(trans->param_flags);
-                       } else if (params.sack_freq > 1) {
-                               trans->sackfreq = params.sack_freq;
-                               trans->param_flags =
-                                       sctp_spp_sackdelay_enable(trans->param_flags);
-                       }
-               }
-       }
+       if (params.sack_assoc_id == SCTP_CURRENT_ASSOC ||
+           params.sack_assoc_id == SCTP_ALL_ASSOC)
+               list_for_each_entry(asoc, &sp->ep->asocs, asocs)
+                       sctp_apply_asoc_delayed_ack(&params, asoc);
 
        return 0;
 }
@@ -2997,15 +3009,22 @@ static int sctp_setsockopt_default_send_param(struct sock *sk,
                return -EINVAL;
 
        asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
-       if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && info.sinfo_assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
+
        if (asoc) {
                asoc->default_stream = info.sinfo_stream;
                asoc->default_flags = info.sinfo_flags;
                asoc->default_ppid = info.sinfo_ppid;
                asoc->default_context = info.sinfo_context;
                asoc->default_timetolive = info.sinfo_timetolive;
-       } else {
+
+               return 0;
+       }
+
+       if (info.sinfo_assoc_id == SCTP_FUTURE_ASSOC ||
+           info.sinfo_assoc_id == SCTP_ALL_ASSOC) {
                sp->default_stream = info.sinfo_stream;
                sp->default_flags = info.sinfo_flags;
                sp->default_ppid = info.sinfo_ppid;
@@ -3013,6 +3032,17 @@ static int sctp_setsockopt_default_send_param(struct sock *sk,
                sp->default_timetolive = info.sinfo_timetolive;
        }
 
+       if (info.sinfo_assoc_id == SCTP_CURRENT_ASSOC ||
+           info.sinfo_assoc_id == SCTP_ALL_ASSOC) {
+               list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
+                       asoc->default_stream = info.sinfo_stream;
+                       asoc->default_flags = info.sinfo_flags;
+                       asoc->default_ppid = info.sinfo_ppid;
+                       asoc->default_context = info.sinfo_context;
+                       asoc->default_timetolive = info.sinfo_timetolive;
+               }
+       }
+
        return 0;
 }
 
@@ -3037,20 +3067,37 @@ static int sctp_setsockopt_default_sndinfo(struct sock *sk,
                return -EINVAL;
 
        asoc = sctp_id2assoc(sk, info.snd_assoc_id);
-       if (!asoc && info.snd_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && info.snd_assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
+
        if (asoc) {
                asoc->default_stream = info.snd_sid;
                asoc->default_flags = info.snd_flags;
                asoc->default_ppid = info.snd_ppid;
                asoc->default_context = info.snd_context;
-       } else {
+
+               return 0;
+       }
+
+       if (info.snd_assoc_id == SCTP_FUTURE_ASSOC ||
+           info.snd_assoc_id == SCTP_ALL_ASSOC) {
                sp->default_stream = info.snd_sid;
                sp->default_flags = info.snd_flags;
                sp->default_ppid = info.snd_ppid;
                sp->default_context = info.snd_context;
        }
 
+       if (info.snd_assoc_id == SCTP_CURRENT_ASSOC ||
+           info.snd_assoc_id == SCTP_ALL_ASSOC) {
+               list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
+                       asoc->default_stream = info.snd_sid;
+                       asoc->default_flags = info.snd_flags;
+                       asoc->default_ppid = info.snd_ppid;
+                       asoc->default_context = info.snd_context;
+               }
+       }
+
        return 0;
 }
 
@@ -3144,7 +3191,8 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, unsigne
        asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
 
        /* Set the values to the specific association */
-       if (!asoc && rtoinfo.srto_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && rtoinfo.srto_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
        rto_max = rtoinfo.srto_max;
@@ -3206,7 +3254,8 @@ static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, unsig
 
        asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
 
-       if (!asoc && assocparams.sasoc_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && assocparams.sasoc_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
        /* Set the values to the specific association */
@@ -3319,7 +3368,7 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
                                    current->comm, task_pid_nr(current));
                if (copy_from_user(&val, optval, optlen))
                        return -EFAULT;
-               params.assoc_id = 0;
+               params.assoc_id = SCTP_FUTURE_ASSOC;
        } else if (optlen == sizeof(struct sctp_assoc_value)) {
                if (copy_from_user(&params, optval, optlen))
                        return -EFAULT;
@@ -3329,6 +3378,9 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
        }
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
+               return -EINVAL;
 
        if (val) {
                int min_len, max_len;
@@ -3346,8 +3398,6 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
                asoc->user_frag = val;
                sctp_assoc_update_frag_point(asoc);
        } else {
-               if (params.assoc_id && sctp_style(sk, UDP))
-                       return -EINVAL;
                sp->user_frag = val;
        }
 
@@ -3460,8 +3510,8 @@ static int sctp_setsockopt_adaptation_layer(struct sock *sk, char __user *optval
 static int sctp_setsockopt_context(struct sock *sk, char __user *optval,
                                   unsigned int optlen)
 {
+       struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_assoc_value params;
-       struct sctp_sock *sp;
        struct sctp_association *asoc;
 
        if (optlen != sizeof(struct sctp_assoc_value))
@@ -3469,17 +3519,26 @@ static int sctp_setsockopt_context(struct sock *sk, char __user *optval,
        if (copy_from_user(&params, optval, optlen))
                return -EFAULT;
 
-       sp = sctp_sk(sk);
+       asoc = sctp_id2assoc(sk, params.assoc_id);
+       if (!asoc && params.assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
+               return -EINVAL;
 
-       if (params.assoc_id != 0) {
-               asoc = sctp_id2assoc(sk, params.assoc_id);
-               if (!asoc)
-                       return -EINVAL;
+       if (asoc) {
                asoc->default_rcv_context = params.assoc_value;
-       } else {
-               sp->default_rcv_context = params.assoc_value;
+
+               return 0;
        }
 
+       if (params.assoc_id == SCTP_FUTURE_ASSOC ||
+           params.assoc_id == SCTP_ALL_ASSOC)
+               sp->default_rcv_context = params.assoc_value;
+
+       if (params.assoc_id == SCTP_CURRENT_ASSOC ||
+           params.assoc_id == SCTP_ALL_ASSOC)
+               list_for_each_entry(asoc, &sp->ep->asocs, asocs)
+                       asoc->default_rcv_context = params.assoc_value;
+
        return 0;
 }
 
@@ -3580,11 +3639,9 @@ static int sctp_setsockopt_maxburst(struct sock *sk,
                                    char __user *optval,
                                    unsigned int optlen)
 {
+       struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_assoc_value params;
-       struct sctp_sock *sp;
        struct sctp_association *asoc;
-       int val;
-       int assoc_id = 0;
 
        if (optlen == sizeof(int)) {
                pr_warn_ratelimited(DEPRECATED
@@ -3592,25 +3649,34 @@ static int sctp_setsockopt_maxburst(struct sock *sk,
                                    "Use of int in max_burst socket option deprecated.\n"
                                    "Use struct sctp_assoc_value instead\n",
                                    current->comm, task_pid_nr(current));
-               if (copy_from_user(&val, optval, optlen))
+               if (copy_from_user(&params.assoc_value, optval, optlen))
                        return -EFAULT;
+               params.assoc_id = SCTP_FUTURE_ASSOC;
        } else if (optlen == sizeof(struct sctp_assoc_value)) {
                if (copy_from_user(&params, optval, optlen))
                        return -EFAULT;
-               val = params.assoc_value;
-               assoc_id = params.assoc_id;
        } else
                return -EINVAL;
 
-       sp = sctp_sk(sk);
+       asoc = sctp_id2assoc(sk, params.assoc_id);
+       if (!asoc && params.assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
+               return -EINVAL;
 
-       if (assoc_id != 0) {
-               asoc = sctp_id2assoc(sk, assoc_id);
-               if (!asoc)
-                       return -EINVAL;
-               asoc->max_burst = val;
-       } else
-               sp->max_burst = val;
+       if (asoc) {
+               asoc->max_burst = params.assoc_value;
+
+               return 0;
+       }
+
+       if (params.assoc_id == SCTP_FUTURE_ASSOC ||
+           params.assoc_id == SCTP_ALL_ASSOC)
+               sp->max_burst = params.assoc_value;
+
+       if (params.assoc_id == SCTP_CURRENT_ASSOC ||
+           params.assoc_id == SCTP_ALL_ASSOC)
+               list_for_each_entry(asoc, &sp->ep->asocs, asocs)
+                       asoc->max_burst = params.assoc_value;
 
        return 0;
 }
@@ -3702,7 +3768,7 @@ static int sctp_setsockopt_auth_key(struct sock *sk,
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
        struct sctp_authkey *authkey;
        struct sctp_association *asoc;
-       int ret;
+       int ret = -EINVAL;
 
        if (!ep->auth_enable)
                return -EACCES;
@@ -3712,25 +3778,44 @@ static int sctp_setsockopt_auth_key(struct sock *sk,
        /* authkey->sca_keylength is u16, so optlen can't be bigger than
         * this.
         */
-       optlen = min_t(unsigned int, optlen, USHRT_MAX +
-                                            sizeof(struct sctp_authkey));
+       optlen = min_t(unsigned int, optlen, USHRT_MAX + sizeof(*authkey));
 
        authkey = memdup_user(optval, optlen);
        if (IS_ERR(authkey))
                return PTR_ERR(authkey);
 
-       if (authkey->sca_keylength > optlen - sizeof(struct sctp_authkey)) {
-               ret = -EINVAL;
+       if (authkey->sca_keylength > optlen - sizeof(*authkey))
                goto out;
-       }
 
        asoc = sctp_id2assoc(sk, authkey->sca_assoc_id);
-       if (!asoc && authkey->sca_assoc_id && sctp_style(sk, UDP)) {
-               ret = -EINVAL;
+       if (!asoc && authkey->sca_assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
+               goto out;
+
+       if (asoc) {
+               ret = sctp_auth_set_key(ep, asoc, authkey);
                goto out;
        }
 
-       ret = sctp_auth_set_key(ep, asoc, authkey);
+       if (authkey->sca_assoc_id == SCTP_FUTURE_ASSOC ||
+           authkey->sca_assoc_id == SCTP_ALL_ASSOC) {
+               ret = sctp_auth_set_key(ep, asoc, authkey);
+               if (ret)
+                       goto out;
+       }
+
+       ret = 0;
+
+       if (authkey->sca_assoc_id == SCTP_CURRENT_ASSOC ||
+           authkey->sca_assoc_id == SCTP_ALL_ASSOC) {
+               list_for_each_entry(asoc, &ep->asocs, asocs) {
+                       int res = sctp_auth_set_key(ep, asoc, authkey);
+
+                       if (res && !ret)
+                               ret = res;
+               }
+       }
+
 out:
        kzfree(authkey);
        return ret;
@@ -3747,8 +3832,9 @@ static int sctp_setsockopt_active_key(struct sock *sk,
                                      unsigned int optlen)
 {
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
-       struct sctp_authkeyid val;
        struct sctp_association *asoc;
+       struct sctp_authkeyid val;
+       int ret = 0;
 
        if (!ep->auth_enable)
                return -EACCES;
@@ -3759,10 +3845,32 @@ static int sctp_setsockopt_active_key(struct sock *sk,
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, val.scact_assoc_id);
-       if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && val.scact_assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
-       return sctp_auth_set_active_key(ep, asoc, val.scact_keynumber);
+       if (asoc)
+               return sctp_auth_set_active_key(ep, asoc, val.scact_keynumber);
+
+       if (val.scact_assoc_id == SCTP_FUTURE_ASSOC ||
+           val.scact_assoc_id == SCTP_ALL_ASSOC) {
+               ret = sctp_auth_set_active_key(ep, asoc, val.scact_keynumber);
+               if (ret)
+                       return ret;
+       }
+
+       if (val.scact_assoc_id == SCTP_CURRENT_ASSOC ||
+           val.scact_assoc_id == SCTP_ALL_ASSOC) {
+               list_for_each_entry(asoc, &ep->asocs, asocs) {
+                       int res = sctp_auth_set_active_key(ep, asoc,
+                                                          val.scact_keynumber);
+
+                       if (res && !ret)
+                               ret = res;
+               }
+       }
+
+       return ret;
 }
 
 /*
@@ -3775,8 +3883,9 @@ static int sctp_setsockopt_del_key(struct sock *sk,
                                   unsigned int optlen)
 {
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
-       struct sctp_authkeyid val;
        struct sctp_association *asoc;
+       struct sctp_authkeyid val;
+       int ret = 0;
 
        if (!ep->auth_enable)
                return -EACCES;
@@ -3787,11 +3896,32 @@ static int sctp_setsockopt_del_key(struct sock *sk,
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, val.scact_assoc_id);
-       if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && val.scact_assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
-       return sctp_auth_del_key_id(ep, asoc, val.scact_keynumber);
+       if (asoc)
+               return sctp_auth_del_key_id(ep, asoc, val.scact_keynumber);
+
+       if (val.scact_assoc_id == SCTP_FUTURE_ASSOC ||
+           val.scact_assoc_id == SCTP_ALL_ASSOC) {
+               ret = sctp_auth_del_key_id(ep, asoc, val.scact_keynumber);
+               if (ret)
+                       return ret;
+       }
+
+       if (val.scact_assoc_id == SCTP_CURRENT_ASSOC ||
+           val.scact_assoc_id == SCTP_ALL_ASSOC) {
+               list_for_each_entry(asoc, &ep->asocs, asocs) {
+                       int res = sctp_auth_del_key_id(ep, asoc,
+                                                      val.scact_keynumber);
 
+                       if (res && !ret)
+                               ret = res;
+               }
+       }
+
+       return ret;
 }
 
 /*
@@ -3803,8 +3933,9 @@ static int sctp_setsockopt_deactivate_key(struct sock *sk, char __user *optval,
                                          unsigned int optlen)
 {
        struct sctp_endpoint *ep = sctp_sk(sk)->ep;
-       struct sctp_authkeyid val;
        struct sctp_association *asoc;
+       struct sctp_authkeyid val;
+       int ret = 0;
 
        if (!ep->auth_enable)
                return -EACCES;
@@ -3815,10 +3946,32 @@ static int sctp_setsockopt_deactivate_key(struct sock *sk, char __user *optval,
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, val.scact_assoc_id);
-       if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && val.scact_assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
-       return sctp_auth_deact_key_id(ep, asoc, val.scact_keynumber);
+       if (asoc)
+               return sctp_auth_deact_key_id(ep, asoc, val.scact_keynumber);
+
+       if (val.scact_assoc_id == SCTP_FUTURE_ASSOC ||
+           val.scact_assoc_id == SCTP_ALL_ASSOC) {
+               ret = sctp_auth_deact_key_id(ep, asoc, val.scact_keynumber);
+               if (ret)
+                       return ret;
+       }
+
+       if (val.scact_assoc_id == SCTP_CURRENT_ASSOC ||
+           val.scact_assoc_id == SCTP_ALL_ASSOC) {
+               list_for_each_entry(asoc, &ep->asocs, asocs) {
+                       int res = sctp_auth_deact_key_id(ep, asoc,
+                                                        val.scact_keynumber);
+
+                       if (res && !ret)
+                               ret = res;
+               }
+       }
+
+       return ret;
 }
 
 /*
@@ -3884,11 +4037,25 @@ static int sctp_setsockopt_paddr_thresholds(struct sock *sk,
                           sizeof(struct sctp_paddrthlds)))
                return -EFAULT;
 
-
-       if (sctp_is_any(sk, (const union sctp_addr *)&val.spt_address)) {
-               asoc = sctp_id2assoc(sk, val.spt_assoc_id);
-               if (!asoc)
+       if (!sctp_is_any(sk, (const union sctp_addr *)&val.spt_address)) {
+               trans = sctp_addr_id2transport(sk, &val.spt_address,
+                                              val.spt_assoc_id);
+               if (!trans)
                        return -ENOENT;
+
+               if (val.spt_pathmaxrxt)
+                       trans->pathmaxrxt = val.spt_pathmaxrxt;
+               trans->pf_retrans = val.spt_pathpfthld;
+
+               return 0;
+       }
+
+       asoc = sctp_id2assoc(sk, val.spt_assoc_id);
+       if (!asoc && val.spt_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
+               return -EINVAL;
+
+       if (asoc) {
                list_for_each_entry(trans, &asoc->peer.transport_addr_list,
                                    transports) {
                        if (val.spt_pathmaxrxt)
@@ -3900,14 +4067,11 @@ static int sctp_setsockopt_paddr_thresholds(struct sock *sk,
                        asoc->pathmaxrxt = val.spt_pathmaxrxt;
                asoc->pf_retrans = val.spt_pathpfthld;
        } else {
-               trans = sctp_addr_id2transport(sk, &val.spt_address,
-                                              val.spt_assoc_id);
-               if (!trans)
-                       return -ENOENT;
+               struct sctp_sock *sp = sctp_sk(sk);
 
                if (val.spt_pathmaxrxt)
-                       trans->pathmaxrxt = val.spt_pathmaxrxt;
-               trans->pf_retrans = val.spt_pathpfthld;
+                       sp->pathmaxrxt = val.spt_pathmaxrxt;
+               sp->pf_retrans = val.spt_pathpfthld;
        }
 
        return 0;
@@ -3950,6 +4114,7 @@ static int sctp_setsockopt_pr_supported(struct sock *sk,
                                        unsigned int optlen)
 {
        struct sctp_assoc_value params;
+       struct sctp_association *asoc;
 
        if (optlen != sizeof(params))
                return -EINVAL;
@@ -3957,6 +4122,11 @@ static int sctp_setsockopt_pr_supported(struct sock *sk,
        if (copy_from_user(&params, optval, optlen))
                return -EFAULT;
 
+       asoc = sctp_id2assoc(sk, params.assoc_id);
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
+               return -EINVAL;
+
        sctp_sk(sk)->ep->prsctp_enable = !!params.assoc_value;
 
        return 0;
@@ -3966,6 +4136,7 @@ static int sctp_setsockopt_default_prinfo(struct sock *sk,
                                          char __user *optval,
                                          unsigned int optlen)
 {
+       struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_default_prinfo info;
        struct sctp_association *asoc;
        int retval = -EINVAL;
@@ -3985,19 +4156,31 @@ static int sctp_setsockopt_default_prinfo(struct sock *sk,
                info.pr_value = 0;
 
        asoc = sctp_id2assoc(sk, info.pr_assoc_id);
+       if (!asoc && info.pr_assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
+               goto out;
+
+       retval = 0;
+
        if (asoc) {
                SCTP_PR_SET_POLICY(asoc->default_flags, info.pr_policy);
                asoc->default_timetolive = info.pr_value;
-       } else if (!info.pr_assoc_id) {
-               struct sctp_sock *sp = sctp_sk(sk);
+               goto out;
+       }
 
+       if (info.pr_assoc_id == SCTP_FUTURE_ASSOC ||
+           info.pr_assoc_id == SCTP_ALL_ASSOC) {
                SCTP_PR_SET_POLICY(sp->default_flags, info.pr_policy);
                sp->default_timetolive = info.pr_value;
-       } else {
-               goto out;
        }
 
-       retval = 0;
+       if (info.pr_assoc_id == SCTP_CURRENT_ASSOC ||
+           info.pr_assoc_id == SCTP_ALL_ASSOC) {
+               list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
+                       SCTP_PR_SET_POLICY(asoc->default_flags, info.pr_policy);
+                       asoc->default_timetolive = info.pr_value;
+               }
+       }
 
 out:
        return retval;
@@ -4020,15 +4203,14 @@ static int sctp_setsockopt_reconfig_supported(struct sock *sk,
        }
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
-       if (asoc) {
-               asoc->reconf_enable = !!params.assoc_value;
-       } else if (!params.assoc_id) {
-               struct sctp_sock *sp = sctp_sk(sk);
-
-               sp->ep->reconf_enable = !!params.assoc_value;
-       } else {
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                goto out;
-       }
+
+       if (asoc)
+               asoc->reconf_enable = !!params.assoc_value;
+       else
+               sctp_sk(sk)->ep->reconf_enable = !!params.assoc_value;
 
        retval = 0;
 
@@ -4040,6 +4222,7 @@ static int sctp_setsockopt_enable_strreset(struct sock *sk,
                                           char __user *optval,
                                           unsigned int optlen)
 {
+       struct sctp_endpoint *ep = sctp_sk(sk)->ep;
        struct sctp_assoc_value params;
        struct sctp_association *asoc;
        int retval = -EINVAL;
@@ -4056,17 +4239,25 @@ static int sctp_setsockopt_enable_strreset(struct sock *sk,
                goto out;
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
+       if (!asoc && params.assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
+               goto out;
+
+       retval = 0;
+
        if (asoc) {
                asoc->strreset_enable = params.assoc_value;
-       } else if (!params.assoc_id) {
-               struct sctp_sock *sp = sctp_sk(sk);
-
-               sp->ep->strreset_enable = params.assoc_value;
-       } else {
                goto out;
        }
 
-       retval = 0;
+       if (params.assoc_id == SCTP_FUTURE_ASSOC ||
+           params.assoc_id == SCTP_ALL_ASSOC)
+               ep->strreset_enable = params.assoc_value;
+
+       if (params.assoc_id == SCTP_CURRENT_ASSOC ||
+           params.assoc_id == SCTP_ALL_ASSOC)
+               list_for_each_entry(asoc, &ep->asocs, asocs)
+                       asoc->strreset_enable = params.assoc_value;
 
 out:
        return retval;
@@ -4161,29 +4352,44 @@ static int sctp_setsockopt_scheduler(struct sock *sk,
                                     char __user *optval,
                                     unsigned int optlen)
 {
+       struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_association *asoc;
        struct sctp_assoc_value params;
-       int retval = -EINVAL;
+       int retval = 0;
 
        if (optlen < sizeof(params))
-               goto out;
+               return -EINVAL;
 
        optlen = sizeof(params);
-       if (copy_from_user(&params, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
+       if (copy_from_user(&params, optval, optlen))
+               return -EFAULT;
 
        if (params.assoc_value > SCTP_SS_MAX)
-               goto out;
+               return -EINVAL;
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
-       if (!asoc)
-               goto out;
+       if (!asoc && params.assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
+               return -EINVAL;
+
+       if (asoc)
+               return sctp_sched_set_sched(asoc, params.assoc_value);
 
-       retval = sctp_sched_set_sched(asoc, params.assoc_value);
+       if (params.assoc_id == SCTP_FUTURE_ASSOC ||
+           params.assoc_id == SCTP_ALL_ASSOC)
+               sp->default_ss = params.assoc_value;
+
+       if (params.assoc_id == SCTP_CURRENT_ASSOC ||
+           params.assoc_id == SCTP_ALL_ASSOC) {
+               list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
+                       int ret = sctp_sched_set_sched(asoc,
+                                                      params.assoc_value);
+
+                       if (ret && !retval)
+                               retval = ret;
+               }
+       }
 
-out:
        return retval;
 }
 
@@ -4191,8 +4397,8 @@ static int sctp_setsockopt_scheduler_value(struct sock *sk,
                                           char __user *optval,
                                           unsigned int optlen)
 {
-       struct sctp_association *asoc;
        struct sctp_stream_value params;
+       struct sctp_association *asoc;
        int retval = -EINVAL;
 
        if (optlen < sizeof(params))
@@ -4205,11 +4411,24 @@ static int sctp_setsockopt_scheduler_value(struct sock *sk,
        }
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
-       if (!asoc)
+       if (!asoc && params.assoc_id != SCTP_CURRENT_ASSOC &&
+           sctp_style(sk, UDP))
                goto out;
 
-       retval = sctp_sched_set_value(asoc, params.stream_id,
-                                     params.stream_value, GFP_KERNEL);
+       if (asoc) {
+               retval = sctp_sched_set_value(asoc, params.stream_id,
+                                             params.stream_value, GFP_KERNEL);
+               goto out;
+       }
+
+       retval = 0;
+
+       list_for_each_entry(asoc, &sctp_sk(sk)->ep->asocs, asocs) {
+               int ret = sctp_sched_set_value(asoc, params.stream_id,
+                                              params.stream_value, GFP_KERNEL);
+               if (ret && !retval) /* try to return the 1st error. */
+                       retval = ret;
+       }
 
 out:
        return retval;
@@ -4220,8 +4439,8 @@ static int sctp_setsockopt_interleaving_supported(struct sock *sk,
                                                  unsigned int optlen)
 {
        struct sctp_sock *sp = sctp_sk(sk);
-       struct net *net = sock_net(sk);
        struct sctp_assoc_value params;
+       struct sctp_association *asoc;
        int retval = -EINVAL;
 
        if (optlen < sizeof(params))
@@ -4233,10 +4452,12 @@ static int sctp_setsockopt_interleaving_supported(struct sock *sk,
                goto out;
        }
 
-       if (params.assoc_id)
+       asoc = sctp_id2assoc(sk, params.assoc_id);
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                goto out;
 
-       if (!net->sctp.intl_enable || !sp->frag_interleave) {
+       if (!sock_net(sk)->sctp.intl_enable || !sp->frag_interleave) {
                retval = -EPERM;
                goto out;
        }
@@ -4271,54 +4492,69 @@ static int sctp_setsockopt_reuse_port(struct sock *sk, char __user *optval,
        return 0;
 }
 
+static int sctp_assoc_ulpevent_type_set(struct sctp_event *param,
+                                       struct sctp_association *asoc)
+{
+       struct sctp_ulpevent *event;
+
+       sctp_ulpevent_type_set(&asoc->subscribe, param->se_type, param->se_on);
+
+       if (param->se_type == SCTP_SENDER_DRY_EVENT && param->se_on) {
+               if (sctp_outq_is_empty(&asoc->outqueue)) {
+                       event = sctp_ulpevent_make_sender_dry_event(asoc,
+                                       GFP_USER | __GFP_NOWARN);
+                       if (!event)
+                               return -ENOMEM;
+
+                       asoc->stream.si->enqueue_event(&asoc->ulpq, event);
+               }
+       }
+
+       return 0;
+}
+
 static int sctp_setsockopt_event(struct sock *sk, char __user *optval,
                                 unsigned int optlen)
 {
+       struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_association *asoc;
-       struct sctp_ulpevent *event;
        struct sctp_event param;
        int retval = 0;
 
-       if (optlen < sizeof(param)) {
-               retval = -EINVAL;
-               goto out;
-       }
+       if (optlen < sizeof(param))
+               return -EINVAL;
 
        optlen = sizeof(param);
-       if (copy_from_user(&param, optval, optlen)) {
-               retval = -EFAULT;
-               goto out;
-       }
+       if (copy_from_user(&param, optval, optlen))
+               return -EFAULT;
 
        if (param.se_type < SCTP_SN_TYPE_BASE ||
-           param.se_type > SCTP_SN_TYPE_MAX) {
-               retval = -EINVAL;
-               goto out;
-       }
+           param.se_type > SCTP_SN_TYPE_MAX)
+               return -EINVAL;
 
        asoc = sctp_id2assoc(sk, param.se_assoc_id);
-       if (!asoc) {
-               sctp_ulpevent_type_set(&sctp_sk(sk)->subscribe,
-                                      param.se_type, param.se_on);
-               goto out;
-       }
+       if (!asoc && param.se_assoc_id > SCTP_ALL_ASSOC &&
+           sctp_style(sk, UDP))
+               return -EINVAL;
 
-       sctp_ulpevent_type_set(&asoc->subscribe, param.se_type, param.se_on);
+       if (asoc)
+               return sctp_assoc_ulpevent_type_set(&param, asoc);
 
-       if (param.se_type == SCTP_SENDER_DRY_EVENT && param.se_on) {
-               if (sctp_outq_is_empty(&asoc->outqueue)) {
-                       event = sctp_ulpevent_make_sender_dry_event(asoc,
-                                       GFP_USER | __GFP_NOWARN);
-                       if (!event) {
-                               retval = -ENOMEM;
-                               goto out;
-                       }
+       if (param.se_assoc_id == SCTP_FUTURE_ASSOC ||
+           param.se_assoc_id == SCTP_ALL_ASSOC)
+               sctp_ulpevent_type_set(&sp->subscribe,
+                                      param.se_type, param.se_on);
 
-                       asoc->stream.si->enqueue_event(&asoc->ulpq, event);
+       if (param.se_assoc_id == SCTP_CURRENT_ASSOC ||
+           param.se_assoc_id == SCTP_ALL_ASSOC) {
+               list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
+                       int ret = sctp_assoc_ulpevent_type_set(&param, asoc);
+
+                       if (ret && !retval)
+                               retval = ret;
                }
        }
 
-out:
        return retval;
 }
 
@@ -4777,12 +5013,14 @@ static int sctp_init_sock(struct sock *sk)
         */
        sp->hbinterval  = net->sctp.hb_interval;
        sp->pathmaxrxt  = net->sctp.max_retrans_path;
+       sp->pf_retrans  = net->sctp.pf_retrans;
        sp->pathmtu     = 0; /* allow default discovery */
        sp->sackdelay   = net->sctp.sack_timeout;
        sp->sackfreq    = 2;
        sp->param_flags = SPP_HB_ENABLE |
                          SPP_PMTUD_ENABLE |
                          SPP_SACKDELAY_ENABLE;
+       sp->default_ss = SCTP_SS_DEFAULT;
 
        /* If enabled no SCTP message fragmentation will be performed.
         * Configure through SCTP_DISABLE_FRAGMENTS socket option.
@@ -5676,12 +5914,13 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
                }
        }
 
-       /* Get association, if assoc_id != 0 and the socket is a one
-        * to many style socket, and an association was not found, then
-        * the id was invalid.
+       /* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the
+        * socket is a one to many style socket, and an association
+        * was not found, then the id was invalid.
         */
        asoc = sctp_id2assoc(sk, params.spp_assoc_id);
-       if (!asoc && params.spp_assoc_id && sctp_style(sk, UDP)) {
+       if (!asoc && params.spp_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP)) {
                pr_debug("%s: failed no association\n", __func__);
                return -EINVAL;
        }
@@ -5810,19 +6049,19 @@ static int sctp_getsockopt_delayed_ack(struct sock *sk, int len,
        } else
                return -EINVAL;
 
-       /* Get association, if sack_assoc_id != 0 and the socket is a one
-        * to many style socket, and an association was not found, then
-        * the id was invalid.
+       /* Get association, if sack_assoc_id != SCTP_FUTURE_ASSOC and the
+        * socket is a one to many style socket, and an association
+        * was not found, then the id was invalid.
         */
        asoc = sctp_id2assoc(sk, params.sack_assoc_id);
-       if (!asoc && params.sack_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && params.sack_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
        if (asoc) {
                /* Fetch association values. */
                if (asoc->param_flags & SPP_SACKDELAY_ENABLE) {
-                       params.sack_delay = jiffies_to_msecs(
-                               asoc->sackdelay);
+                       params.sack_delay = jiffies_to_msecs(asoc->sackdelay);
                        params.sack_freq = asoc->sackfreq;
 
                } else {
@@ -6175,8 +6414,10 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
-       if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && info.sinfo_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
+
        if (asoc) {
                info.sinfo_stream = asoc->default_stream;
                info.sinfo_flags = asoc->default_flags;
@@ -6219,8 +6460,10 @@ static int sctp_getsockopt_default_sndinfo(struct sock *sk, int len,
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, info.snd_assoc_id);
-       if (!asoc && info.snd_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && info.snd_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
+
        if (asoc) {
                info.snd_sid = asoc->default_stream;
                info.snd_flags = asoc->default_flags;
@@ -6296,7 +6539,8 @@ static int sctp_getsockopt_rtoinfo(struct sock *sk, int len,
 
        asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
 
-       if (!asoc && rtoinfo.srto_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && rtoinfo.srto_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
        /* Values corresponding to the specific association. */
@@ -6353,7 +6597,8 @@ static int sctp_getsockopt_associnfo(struct sock *sk, int len,
 
        asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
 
-       if (!asoc && assocparams.sasoc_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && assocparams.sasoc_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
        /* Values correspoinding to the specific association */
@@ -6428,7 +6673,6 @@ static int sctp_getsockopt_context(struct sock *sk, int len,
                                   char __user *optval, int __user *optlen)
 {
        struct sctp_assoc_value params;
-       struct sctp_sock *sp;
        struct sctp_association *asoc;
 
        if (len < sizeof(struct sctp_assoc_value))
@@ -6439,16 +6683,13 @@ static int sctp_getsockopt_context(struct sock *sk, int len,
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
-       sp = sctp_sk(sk);
+       asoc = sctp_id2assoc(sk, params.assoc_id);
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
+               return -EINVAL;
 
-       if (params.assoc_id != 0) {
-               asoc = sctp_id2assoc(sk, params.assoc_id);
-               if (!asoc)
-                       return -EINVAL;
-               params.assoc_value = asoc->default_rcv_context;
-       } else {
-               params.assoc_value = sp->default_rcv_context;
-       }
+       params.assoc_value = asoc ? asoc->default_rcv_context
+                                 : sctp_sk(sk)->default_rcv_context;
 
        if (put_user(len, optlen))
                return -EFAULT;
@@ -6497,7 +6738,7 @@ static int sctp_getsockopt_maxseg(struct sock *sk, int len,
                                    "Use of int in maxseg socket option.\n"
                                    "Use struct sctp_assoc_value instead\n",
                                    current->comm, task_pid_nr(current));
-               params.assoc_id = 0;
+               params.assoc_id = SCTP_FUTURE_ASSOC;
        } else if (len >= sizeof(struct sctp_assoc_value)) {
                len = sizeof(struct sctp_assoc_value);
                if (copy_from_user(&params, optval, len))
@@ -6506,7 +6747,8 @@ static int sctp_getsockopt_maxseg(struct sock *sk, int len,
                return -EINVAL;
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
-       if (!asoc && params.assoc_id && sctp_style(sk, UDP))
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
        if (asoc)
@@ -6583,7 +6825,6 @@ static int sctp_getsockopt_maxburst(struct sock *sk, int len,
                                    int __user *optlen)
 {
        struct sctp_assoc_value params;
-       struct sctp_sock *sp;
        struct sctp_association *asoc;
 
        if (len == sizeof(int)) {
@@ -6592,7 +6833,7 @@ static int sctp_getsockopt_maxburst(struct sock *sk, int len,
                                    "Use of int in max_burst socket option.\n"
                                    "Use struct sctp_assoc_value instead\n",
                                    current->comm, task_pid_nr(current));
-               params.assoc_id = 0;
+               params.assoc_id = SCTP_FUTURE_ASSOC;
        } else if (len >= sizeof(struct sctp_assoc_value)) {
                len = sizeof(struct sctp_assoc_value);
                if (copy_from_user(&params, optval, len))
@@ -6600,15 +6841,12 @@ static int sctp_getsockopt_maxburst(struct sock *sk, int len,
        } else
                return -EINVAL;
 
-       sp = sctp_sk(sk);
+       asoc = sctp_id2assoc(sk, params.assoc_id);
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
+               return -EINVAL;
 
-       if (params.assoc_id != 0) {
-               asoc = sctp_id2assoc(sk, params.assoc_id);
-               if (!asoc)
-                       return -EINVAL;
-               params.assoc_value = asoc->max_burst;
-       } else
-               params.assoc_value = sp->max_burst;
+       params.assoc_value = asoc ? asoc->max_burst : sctp_sk(sk)->max_burst;
 
        if (len == sizeof(int)) {
                if (copy_to_user(optval, &params.assoc_value, len))
@@ -6759,14 +6997,12 @@ static int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len,
 
        to = p->gauth_chunks;
        asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
-       if (!asoc && val.gauth_assoc_id && sctp_style(sk, UDP))
+       if (!asoc && val.gauth_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
                return -EINVAL;
 
-       if (asoc)
-               ch = (struct sctp_chunks_param *)asoc->c.auth_chunks;
-       else
-               ch = ep->auth_chunk_list;
-
+       ch = asoc ? (struct sctp_chunks_param *)asoc->c.auth_chunks
+                 : ep->auth_chunk_list;
        if (!ch)
                goto num;
 
@@ -6911,14 +7147,7 @@ static int sctp_getsockopt_paddr_thresholds(struct sock *sk,
        if (copy_from_user(&val, (struct sctp_paddrthlds __user *)optval, len))
                return -EFAULT;
 
-       if (sctp_is_any(sk, (const union sctp_addr *)&val.spt_address)) {
-               asoc = sctp_id2assoc(sk, val.spt_assoc_id);
-               if (!asoc)
-                       return -ENOENT;
-
-               val.spt_pathpfthld = asoc->pf_retrans;
-               val.spt_pathmaxrxt = asoc->pathmaxrxt;
-       } else {
+       if (!sctp_is_any(sk, (const union sctp_addr *)&val.spt_address)) {
                trans = sctp_addr_id2transport(sk, &val.spt_address,
                                               val.spt_assoc_id);
                if (!trans)
@@ -6926,6 +7155,23 @@ static int sctp_getsockopt_paddr_thresholds(struct sock *sk,
 
                val.spt_pathmaxrxt = trans->pathmaxrxt;
                val.spt_pathpfthld = trans->pf_retrans;
+
+               return 0;
+       }
+
+       asoc = sctp_id2assoc(sk, val.spt_assoc_id);
+       if (!asoc && val.spt_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
+               return -EINVAL;
+
+       if (asoc) {
+               val.spt_pathpfthld = asoc->pf_retrans;
+               val.spt_pathmaxrxt = asoc->pathmaxrxt;
+       } else {
+               struct sctp_sock *sp = sctp_sk(sk);
+
+               val.spt_pathpfthld = sp->pf_retrans;
+               val.spt_pathmaxrxt = sp->pathmaxrxt;
        }
 
        if (put_user(len, optlen) || copy_to_user(optval, &val, len))
@@ -7056,17 +7302,15 @@ static int sctp_getsockopt_pr_supported(struct sock *sk, int len,
                goto out;
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
-       if (asoc) {
-               params.assoc_value = asoc->prsctp_enable;
-       } else if (!params.assoc_id) {
-               struct sctp_sock *sp = sctp_sk(sk);
-
-               params.assoc_value = sp->ep->prsctp_enable;
-       } else {
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP)) {
                retval = -EINVAL;
                goto out;
        }
 
+       params.assoc_value = asoc ? asoc->prsctp_enable
+                                 : sctp_sk(sk)->ep->prsctp_enable;
+
        if (put_user(len, optlen))
                goto out;
 
@@ -7097,17 +7341,20 @@ static int sctp_getsockopt_default_prinfo(struct sock *sk, int len,
                goto out;
 
        asoc = sctp_id2assoc(sk, info.pr_assoc_id);
+       if (!asoc && info.pr_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP)) {
+               retval = -EINVAL;
+               goto out;
+       }
+
        if (asoc) {
                info.pr_policy = SCTP_PR_POLICY(asoc->default_flags);
                info.pr_value = asoc->default_timetolive;
-       } else if (!info.pr_assoc_id) {
+       } else {
                struct sctp_sock *sp = sctp_sk(sk);
 
                info.pr_policy = SCTP_PR_POLICY(sp->default_flags);
                info.pr_value = sp->default_timetolive;
-       } else {
-               retval = -EINVAL;
-               goto out;
        }
 
        if (put_user(len, optlen))
@@ -7263,17 +7510,15 @@ static int sctp_getsockopt_reconfig_supported(struct sock *sk, int len,
                goto out;
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
-       if (asoc) {
-               params.assoc_value = asoc->reconf_enable;
-       } else if (!params.assoc_id) {
-               struct sctp_sock *sp = sctp_sk(sk);
-
-               params.assoc_value = sp->ep->reconf_enable;
-       } else {
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP)) {
                retval = -EINVAL;
                goto out;
        }
 
+       params.assoc_value = asoc ? asoc->reconf_enable
+                                 : sctp_sk(sk)->ep->reconf_enable;
+
        if (put_user(len, optlen))
                goto out;
 
@@ -7304,17 +7549,15 @@ static int sctp_getsockopt_enable_strreset(struct sock *sk, int len,
                goto out;
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
-       if (asoc) {
-               params.assoc_value = asoc->strreset_enable;
-       } else if (!params.assoc_id) {
-               struct sctp_sock *sp = sctp_sk(sk);
-
-               params.assoc_value = sp->ep->strreset_enable;
-       } else {
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP)) {
                retval = -EINVAL;
                goto out;
        }
 
+       params.assoc_value = asoc ? asoc->strreset_enable
+                                 : sctp_sk(sk)->ep->strreset_enable;
+
        if (put_user(len, optlen))
                goto out;
 
@@ -7345,12 +7588,14 @@ static int sctp_getsockopt_scheduler(struct sock *sk, int len,
                goto out;
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
-       if (!asoc) {
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP)) {
                retval = -EINVAL;
                goto out;
        }
 
-       params.assoc_value = sctp_sched_get_sched(asoc);
+       params.assoc_value = asoc ? sctp_sched_get_sched(asoc)
+                                 : sctp_sk(sk)->default_ss;
 
        if (put_user(len, optlen))
                goto out;
@@ -7424,17 +7669,15 @@ static int sctp_getsockopt_interleaving_supported(struct sock *sk, int len,
                goto out;
 
        asoc = sctp_id2assoc(sk, params.assoc_id);
-       if (asoc) {
-               params.assoc_value = asoc->intl_enable;
-       } else if (!params.assoc_id) {
-               struct sctp_sock *sp = sctp_sk(sk);
-
-               params.assoc_value = sp->strm_interleave;
-       } else {
+       if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP)) {
                retval = -EINVAL;
                goto out;
        }
 
+       params.assoc_value = asoc ? asoc->intl_enable
+                                 : sctp_sk(sk)->strm_interleave;
+
        if (put_user(len, optlen))
                goto out;
 
@@ -7486,6 +7729,10 @@ static int sctp_getsockopt_event(struct sock *sk, int len, char __user *optval,
                return -EINVAL;
 
        asoc = sctp_id2assoc(sk, param.se_assoc_id);
+       if (!asoc && param.se_assoc_id != SCTP_FUTURE_ASSOC &&
+           sctp_style(sk, UDP))
+               return -EINVAL;
+
        subscribe = asoc ? asoc->subscribe : sctp_sk(sk)->subscribe;
        param.se_on = sctp_ulpevent_type_enabled(subscribe, param.se_type);
 
index 80e0ae5..2936ed1 100644 (file)
@@ -84,6 +84,19 @@ static void fa_zero(struct flex_array *fa, size_t index, size_t count)
        }
 }
 
+static size_t fa_index(struct flex_array *fa, void *elem, size_t count)
+{
+       size_t index = 0;
+
+       while (count--) {
+               if (elem == flex_array_get(fa, index))
+                       break;
+               index++;
+       }
+
+       return index;
+}
+
 /* Migrates chunks from stream queues to new stream queues if needed,
  * but not across associations. Also, removes those chunks to streams
  * higher than the new max.
@@ -131,8 +144,10 @@ static void sctp_stream_outq_migrate(struct sctp_stream *stream,
                }
        }
 
-       for (i = outcnt; i < stream->outcnt; i++)
+       for (i = outcnt; i < stream->outcnt; i++) {
                kfree(SCTP_SO(stream, i)->ext);
+               SCTP_SO(stream, i)->ext = NULL;
+       }
 }
 
 static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt,
@@ -147,6 +162,13 @@ static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt,
 
        if (stream->out) {
                fa_copy(out, stream->out, 0, min(outcnt, stream->outcnt));
+               if (stream->out_curr) {
+                       size_t index = fa_index(stream->out, stream->out_curr,
+                                               stream->outcnt);
+
+                       BUG_ON(index == stream->outcnt);
+                       stream->out_curr = flex_array_get(out, index);
+               }
                fa_free(stream->out);
        }
 
index c4e5660..46fa9f3 100644 (file)
 #include "smc_rx.h"
 #include "smc_close.h"
 
-static DEFINE_MUTEX(smc_create_lgr_pending);   /* serialize link group
-                                                * creation
+static DEFINE_MUTEX(smc_server_lgr_pending);   /* serialize link group
+                                                * creation on server
+                                                */
+static DEFINE_MUTEX(smc_client_lgr_pending);   /* serialize link group
+                                                * creation on client
                                                 */
 
 static void smc_tcp_listen_work(struct work_struct *);
@@ -145,32 +148,33 @@ static int smc_release(struct socket *sock)
                rc = smc_close_active(smc);
                sock_set_flag(sk, SOCK_DEAD);
                sk->sk_shutdown |= SHUTDOWN_MASK;
-       }
-
-       sk->sk_prot->unhash(sk);
-
-       if (smc->clcsock) {
-               if (smc->use_fallback && sk->sk_state == SMC_LISTEN) {
+       } else {
+               if (sk->sk_state != SMC_LISTEN && sk->sk_state != SMC_INIT)
+                       sock_put(sk); /* passive closing */
+               if (sk->sk_state == SMC_LISTEN) {
                        /* wake up clcsock accept */
                        rc = kernel_sock_shutdown(smc->clcsock, SHUT_RDWR);
                }
-               mutex_lock(&smc->clcsock_release_lock);
-               sock_release(smc->clcsock);
-               smc->clcsock = NULL;
-               mutex_unlock(&smc->clcsock_release_lock);
-       }
-       if (smc->use_fallback) {
-               if (sk->sk_state != SMC_LISTEN && sk->sk_state != SMC_INIT)
-                       sock_put(sk); /* passive closing */
                sk->sk_state = SMC_CLOSED;
                sk->sk_state_change(sk);
        }
 
+       sk->sk_prot->unhash(sk);
+
+       if (sk->sk_state == SMC_CLOSED) {
+               if (smc->clcsock) {
+                       mutex_lock(&smc->clcsock_release_lock);
+                       sock_release(smc->clcsock);
+                       smc->clcsock = NULL;
+                       mutex_unlock(&smc->clcsock_release_lock);
+               }
+               if (!smc->use_fallback)
+                       smc_conn_free(&smc->conn);
+       }
+
        /* detach socket */
        sock_orphan(sk);
        sock->sk = NULL;
-       if (!smc->use_fallback && sk->sk_state == SMC_CLOSED)
-               smc_conn_free(&smc->conn);
        release_sock(sk);
 
        sock_put(sk); /* final sock_put */
@@ -291,7 +295,8 @@ static void smc_copy_sock_settings(struct sock *nsk, struct sock *osk,
                             (1UL << SOCK_RXQ_OVFL) | \
                             (1UL << SOCK_WIFI_STATUS) | \
                             (1UL << SOCK_NOFCS) | \
-                            (1UL << SOCK_FILTER_LOCKED))
+                            (1UL << SOCK_FILTER_LOCKED) | \
+                            (1UL << SOCK_TSTAMP_NEW))
 /* copy only relevant settings and flags of SOL_SOCKET level from smc to
  * clc socket (since smc is not called for these options from net/core)
  */
@@ -475,7 +480,12 @@ static int smc_connect_abort(struct smc_sock *smc, int reason_code,
 {
        if (local_contact == SMC_FIRST_CONTACT)
                smc_lgr_forget(smc->conn.lgr);
-       mutex_unlock(&smc_create_lgr_pending);
+       if (smc->conn.lgr->is_smcd)
+               /* there is only one lgr role for SMC-D; use server lock */
+               mutex_unlock(&smc_server_lgr_pending);
+       else
+               mutex_unlock(&smc_client_lgr_pending);
+
        smc_conn_free(&smc->conn);
        return reason_code;
 }
@@ -560,7 +570,7 @@ static int smc_connect_rdma(struct smc_sock *smc,
        struct smc_link *link;
        int reason_code = 0;
 
-       mutex_lock(&smc_create_lgr_pending);
+       mutex_lock(&smc_client_lgr_pending);
        local_contact = smc_conn_create(smc, false, aclc->hdr.flag, ibdev,
                                        ibport, ntoh24(aclc->qpn), &aclc->lcl,
                                        NULL, 0);
@@ -571,7 +581,8 @@ static int smc_connect_rdma(struct smc_sock *smc,
                        reason_code = SMC_CLC_DECL_SYNCERR; /* synchr. error */
                else
                        reason_code = SMC_CLC_DECL_INTERR; /* other error */
-               return smc_connect_abort(smc, reason_code, 0);
+               mutex_unlock(&smc_client_lgr_pending);
+               return reason_code;
        }
        link = &smc->conn.lgr->lnk[SMC_SINGLE_LINK];
 
@@ -615,7 +626,7 @@ static int smc_connect_rdma(struct smc_sock *smc,
                        return smc_connect_abort(smc, reason_code,
                                                 local_contact);
        }
-       mutex_unlock(&smc_create_lgr_pending);
+       mutex_unlock(&smc_client_lgr_pending);
 
        smc_copy_sock_settings_to_clc(smc);
        if (smc->sk.sk_state == SMC_INIT)
@@ -632,11 +643,14 @@ static int smc_connect_ism(struct smc_sock *smc,
        int local_contact = SMC_FIRST_CONTACT;
        int rc = 0;
 
-       mutex_lock(&smc_create_lgr_pending);
+       /* there is only one lgr role for SMC-D; use server lock */
+       mutex_lock(&smc_server_lgr_pending);
        local_contact = smc_conn_create(smc, true, aclc->hdr.flag, NULL, 0, 0,
                                        NULL, ismdev, aclc->gid);
-       if (local_contact < 0)
-               return smc_connect_abort(smc, SMC_CLC_DECL_MEM, 0);
+       if (local_contact < 0) {
+               mutex_unlock(&smc_server_lgr_pending);
+               return SMC_CLC_DECL_MEM;
+       }
 
        /* Create send and receive buffers */
        if (smc_buf_create(smc, true))
@@ -650,7 +664,7 @@ static int smc_connect_ism(struct smc_sock *smc,
        rc = smc_clc_send_confirm(smc);
        if (rc)
                return smc_connect_abort(smc, rc, local_contact);
-       mutex_unlock(&smc_create_lgr_pending);
+       mutex_unlock(&smc_server_lgr_pending);
 
        smc_copy_sock_settings_to_clc(smc);
        if (smc->sk.sk_state == SMC_INIT)
@@ -1249,7 +1263,7 @@ static void smc_listen_work(struct work_struct *work)
                return;
        }
 
-       mutex_lock(&smc_create_lgr_pending);
+       mutex_lock(&smc_server_lgr_pending);
        smc_close_init(new_smc);
        smc_rx_init(new_smc);
        smc_tx_init(new_smc);
@@ -1271,7 +1285,7 @@ static void smc_listen_work(struct work_struct *work)
                                  &local_contact) ||
             smc_listen_rdma_reg(new_smc, local_contact))) {
                /* SMC not supported, decline */
-               mutex_unlock(&smc_create_lgr_pending);
+               mutex_unlock(&smc_server_lgr_pending);
                smc_listen_decline(new_smc, SMC_CLC_DECL_MODEUNSUPP,
                                   local_contact);
                return;
@@ -1280,29 +1294,33 @@ static void smc_listen_work(struct work_struct *work)
        /* send SMC Accept CLC message */
        rc = smc_clc_send_accept(new_smc, local_contact);
        if (rc) {
-               mutex_unlock(&smc_create_lgr_pending);
+               mutex_unlock(&smc_server_lgr_pending);
                smc_listen_decline(new_smc, rc, local_contact);
                return;
        }
 
+       /* SMC-D does not need this lock any more */
+       if (ism_supported)
+               mutex_unlock(&smc_server_lgr_pending);
+
        /* receive SMC Confirm CLC message */
        reason_code = smc_clc_wait_msg(new_smc, &cclc, sizeof(cclc),
                                       SMC_CLC_CONFIRM, CLC_WAIT_TIME);
        if (reason_code) {
-               mutex_unlock(&smc_create_lgr_pending);
+               if (!ism_supported)
+                       mutex_unlock(&smc_server_lgr_pending);
                smc_listen_decline(new_smc, reason_code, local_contact);
                return;
        }
 
        /* finish worker */
        if (!ism_supported) {
-               if (smc_listen_rdma_finish(new_smc, &cclc, local_contact)) {
-                       mutex_unlock(&smc_create_lgr_pending);
+               rc = smc_listen_rdma_finish(new_smc, &cclc, local_contact);
+               mutex_unlock(&smc_server_lgr_pending);
+               if (rc)
                        return;
-               }
        }
        smc_conn_save_peer_info(new_smc, &cclc);
-       mutex_unlock(&smc_create_lgr_pending);
        smc_listen_out_connected(new_smc);
 }
 
@@ -1505,6 +1523,11 @@ static int smc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
 
        smc = smc_sk(sk);
        lock_sock(sk);
+       if (sk->sk_state == SMC_CLOSED && (sk->sk_shutdown & RCV_SHUTDOWN)) {
+               /* socket was connected before, no more data to read */
+               rc = 0;
+               goto out;
+       }
        if ((sk->sk_state == SMC_INIT) ||
            (sk->sk_state == SMC_LISTEN) ||
            (sk->sk_state == SMC_CLOSED))
@@ -1840,7 +1863,11 @@ static ssize_t smc_splice_read(struct socket *sock, loff_t *ppos,
 
        smc = smc_sk(sk);
        lock_sock(sk);
-
+       if (sk->sk_state == SMC_CLOSED && (sk->sk_shutdown & RCV_SHUTDOWN)) {
+               /* socket was connected before, no more data to read */
+               rc = 0;
+               goto out;
+       }
        if (sk->sk_state == SMC_INIT ||
            sk->sk_state == SMC_LISTEN ||
            sk->sk_state == SMC_CLOSED)
index db83332..d0b0f4c 100644 (file)
 
 /********************************** send *************************************/
 
-struct smc_cdc_tx_pend {
-       struct smc_connection   *conn;          /* socket connection */
-       union smc_host_cursor   cursor; /* tx sndbuf cursor sent */
-       union smc_host_cursor   p_cursor;       /* rx RMBE cursor produced */
-       u16                     ctrl_seq;       /* conn. tx sequence # */
-};
-
 /* handler for send/transmission completion of a CDC msg */
 static void smc_cdc_tx_handler(struct smc_wr_tx_pend_priv *pnd_snd,
                               struct smc_link *link,
@@ -61,12 +54,14 @@ static void smc_cdc_tx_handler(struct smc_wr_tx_pend_priv *pnd_snd,
 
 int smc_cdc_get_free_slot(struct smc_connection *conn,
                          struct smc_wr_buf **wr_buf,
+                         struct smc_rdma_wr **wr_rdma_buf,
                          struct smc_cdc_tx_pend **pend)
 {
        struct smc_link *link = &conn->lgr->lnk[SMC_SINGLE_LINK];
        int rc;
 
        rc = smc_wr_tx_get_free_slot(link, smc_cdc_tx_handler, wr_buf,
+                                    wr_rdma_buf,
                                     (struct smc_wr_tx_pend_priv **)pend);
        if (!conn->alert_token_local)
                /* abnormal termination */
@@ -96,6 +91,7 @@ int smc_cdc_msg_send(struct smc_connection *conn,
                     struct smc_wr_buf *wr_buf,
                     struct smc_cdc_tx_pend *pend)
 {
+       union smc_host_cursor cfed;
        struct smc_link *link;
        int rc;
 
@@ -105,12 +101,12 @@ int smc_cdc_msg_send(struct smc_connection *conn,
 
        conn->tx_cdc_seq++;
        conn->local_tx_ctrl.seqno = conn->tx_cdc_seq;
-       smc_host_msg_to_cdc((struct smc_cdc_msg *)wr_buf,
-                           &conn->local_tx_ctrl, conn);
+       smc_host_msg_to_cdc((struct smc_cdc_msg *)wr_buf, conn, &cfed);
        rc = smc_wr_tx_send(link, (struct smc_wr_tx_pend_priv *)pend);
-       if (!rc)
-               smc_curs_copy(&conn->rx_curs_confirmed,
-                             &conn->local_tx_ctrl.cons, conn);
+       if (!rc) {
+               smc_curs_copy(&conn->rx_curs_confirmed, &cfed, conn);
+               conn->local_rx_ctrl.prod_flags.cons_curs_upd_req = 0;
+       }
 
        return rc;
 }
@@ -121,11 +117,14 @@ static int smcr_cdc_get_slot_and_msg_send(struct smc_connection *conn)
        struct smc_wr_buf *wr_buf;
        int rc;
 
-       rc = smc_cdc_get_free_slot(conn, &wr_buf, &pend);
+       rc = smc_cdc_get_free_slot(conn, &wr_buf, NULL, &pend);
        if (rc)
                return rc;
 
-       return smc_cdc_msg_send(conn, wr_buf, pend);
+       spin_lock_bh(&conn->send_lock);
+       rc = smc_cdc_msg_send(conn, wr_buf, pend);
+       spin_unlock_bh(&conn->send_lock);
+       return rc;
 }
 
 int smc_cdc_get_slot_and_msg_send(struct smc_connection *conn)
@@ -195,6 +194,7 @@ int smcd_cdc_msg_send(struct smc_connection *conn)
        if (rc)
                return rc;
        smc_curs_copy(&conn->rx_curs_confirmed, &curs, conn);
+       conn->local_rx_ctrl.prod_flags.cons_curs_upd_req = 0;
        /* Calculate transmitted data and increment free send buffer space */
        diff = smc_curs_diff(conn->sndbuf_desc->len, &conn->tx_curs_fin,
                             &conn->tx_curs_sent);
@@ -271,26 +271,18 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc,
                smp_mb__after_atomic();
                smc->sk.sk_data_ready(&smc->sk);
        } else {
-               if (conn->local_rx_ctrl.prod_flags.write_blocked ||
-                   conn->local_rx_ctrl.prod_flags.cons_curs_upd_req ||
-                   conn->local_rx_ctrl.prod_flags.urg_data_pending) {
-                       if (conn->local_rx_ctrl.prod_flags.urg_data_pending)
-                               conn->urg_state = SMC_URG_NOTYET;
-                       /* force immediate tx of current consumer cursor, but
-                        * under send_lock to guarantee arrival in seqno-order
-                        */
-                       if (smc->sk.sk_state != SMC_INIT)
-                               smc_tx_sndbuf_nonempty(conn);
-               }
+               if (conn->local_rx_ctrl.prod_flags.write_blocked)
+                       smc->sk.sk_data_ready(&smc->sk);
+               if (conn->local_rx_ctrl.prod_flags.urg_data_pending)
+                       conn->urg_state = SMC_URG_NOTYET;
        }
 
-       /* piggy backed tx info */
        /* trigger sndbuf consumer: RDMA write into peer RMBE and CDC */
-       if (diff_cons && smc_tx_prepared_sends(conn)) {
+       if ((diff_cons && smc_tx_prepared_sends(conn)) ||
+           conn->local_rx_ctrl.prod_flags.cons_curs_upd_req ||
+           conn->local_rx_ctrl.prod_flags.urg_data_pending)
                smc_tx_sndbuf_nonempty(conn);
-               /* trigger socket release if connection closed */
-               smc_close_wake_tx_prepared(smc);
-       }
+
        if (diff_cons && conn->urg_tx_pend &&
            atomic_read(&conn->peer_rmbe_space) == conn->peer_rmbe_size) {
                /* urg data confirmed by peer, indicate we're ready for more */
index b5bfe38..861dc24 100644 (file)
@@ -160,7 +160,9 @@ static inline void smcd_curs_copy(union smcd_cdc_cursor *tgt,
 #endif
 }
 
-/* calculate cursor difference between old and new, where old <= new */
+/* calculate cursor difference between old and new, where old <= new and
+ * difference cannot exceed size
+ */
 static inline int smc_curs_diff(unsigned int size,
                                union smc_host_cursor *old,
                                union smc_host_cursor *new)
@@ -185,28 +187,51 @@ static inline int smc_curs_comp(unsigned int size,
        return smc_curs_diff(size, old, new);
 }
 
+/* calculate cursor difference between old and new, where old <= new and
+ * difference may exceed size
+ */
+static inline int smc_curs_diff_large(unsigned int size,
+                                     union smc_host_cursor *old,
+                                     union smc_host_cursor *new)
+{
+       if (old->wrap < new->wrap)
+               return min_t(int,
+                            (size - old->count) + new->count +
+                            (new->wrap - old->wrap - 1) * size,
+                            size);
+
+       if (old->wrap > new->wrap) /* wrap has switched from 0xffff to 0x0000 */
+               return min_t(int,
+                            (size - old->count) + new->count +
+                            (new->wrap + 0xffff - old->wrap) * size,
+                            size);
+
+       return max_t(int, 0, (new->count - old->count));
+}
+
 static inline void smc_host_cursor_to_cdc(union smc_cdc_cursor *peer,
                                          union smc_host_cursor *local,
+                                         union smc_host_cursor *save,
                                          struct smc_connection *conn)
 {
-       union smc_host_cursor temp;
-
-       smc_curs_copy(&temp, local, conn);
-       peer->count = htonl(temp.count);
-       peer->wrap = htons(temp.wrap);
+       smc_curs_copy(save, local, conn);
+       peer->count = htonl(save->count);
+       peer->wrap = htons(save->wrap);
        /* peer->reserved = htons(0); must be ensured by caller */
 }
 
 static inline void smc_host_msg_to_cdc(struct smc_cdc_msg *peer,
-                                      struct smc_host_cdc_msg *local,
-                                      struct smc_connection *conn)
+                                      struct smc_connection *conn,
+                                      union smc_host_cursor *save)
 {
+       struct smc_host_cdc_msg *local = &conn->local_tx_ctrl;
+
        peer->common.type = local->common.type;
        peer->len = local->len;
        peer->seqno = htons(local->seqno);
        peer->token = htonl(local->token);
-       smc_host_cursor_to_cdc(&peer->prod, &local->prod, conn);
-       smc_host_cursor_to_cdc(&peer->cons, &local->cons, conn);
+       smc_host_cursor_to_cdc(&peer->prod, &local->prod, save, conn);
+       smc_host_cursor_to_cdc(&peer->cons, &local->cons, save, conn);
        peer->prod_flags = local->prod_flags;
        peer->conn_state_flags = local->conn_state_flags;
 }
@@ -245,17 +270,18 @@ static inline void smcr_cdc_msg_to_host(struct smc_host_cdc_msg *local,
 }
 
 static inline void smcd_cdc_msg_to_host(struct smc_host_cdc_msg *local,
-                                       struct smcd_cdc_msg *peer)
+                                       struct smcd_cdc_msg *peer,
+                                       struct smc_connection *conn)
 {
        union smc_host_cursor temp;
 
        temp.wrap = peer->prod.wrap;
        temp.count = peer->prod.count;
-       atomic64_set(&local->prod.acurs, atomic64_read(&temp.acurs));
+       smc_curs_copy(&local->prod, &temp, conn);
 
        temp.wrap = peer->cons.wrap;
        temp.count = peer->cons.count;
-       atomic64_set(&local->cons.acurs, atomic64_read(&temp.acurs));
+       smc_curs_copy(&local->cons, &temp, conn);
        local->prod_flags = peer->cons.prod_flags;
        local->conn_state_flags = peer->cons.conn_state_flags;
 }
@@ -265,15 +291,21 @@ static inline void smc_cdc_msg_to_host(struct smc_host_cdc_msg *local,
                                       struct smc_connection *conn)
 {
        if (conn->lgr->is_smcd)
-               smcd_cdc_msg_to_host(local, (struct smcd_cdc_msg *)peer);
+               smcd_cdc_msg_to_host(local, (struct smcd_cdc_msg *)peer, conn);
        else
                smcr_cdc_msg_to_host(local, peer, conn);
 }
 
-struct smc_cdc_tx_pend;
+struct smc_cdc_tx_pend {
+       struct smc_connection   *conn;          /* socket connection */
+       union smc_host_cursor   cursor;         /* tx sndbuf cursor sent */
+       union smc_host_cursor   p_cursor;       /* rx RMBE cursor produced */
+       u16                     ctrl_seq;       /* conn. tx sequence # */
+};
 
 int smc_cdc_get_free_slot(struct smc_connection *conn,
                          struct smc_wr_buf **wr_buf,
+                         struct smc_rdma_wr **wr_rdma_buf,
                          struct smc_cdc_tx_pend **pend);
 void smc_cdc_tx_dismiss_slots(struct smc_connection *conn);
 int smc_cdc_msg_send(struct smc_connection *conn, struct smc_wr_buf *wr_buf,
index 776e9df..d53fd58 100644 (file)
@@ -378,7 +378,7 @@ int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info)
        vec.iov_len = sizeof(struct smc_clc_msg_decline);
        len = kernel_sendmsg(smc->clcsock, &msg, &vec, 1,
                             sizeof(struct smc_clc_msg_decline));
-       if (len < sizeof(struct smc_clc_msg_decline))
+       if (len < 0 || len < sizeof(struct smc_clc_msg_decline))
                len = -EPROTO;
        return len > 0 ? 0 : len;
 }
index ea2b87f..2ad37e9 100644 (file)
@@ -345,14 +345,7 @@ static void smc_close_passive_work(struct work_struct *work)
 
        switch (sk->sk_state) {
        case SMC_INIT:
-               if (atomic_read(&conn->bytes_to_rcv) ||
-                   (rxflags->peer_done_writing &&
-                    !smc_cdc_rxed_any_close(conn))) {
-                       sk->sk_state = SMC_APPCLOSEWAIT1;
-               } else {
-                       sk->sk_state = SMC_CLOSED;
-                       sock_put(sk); /* passive closing */
-               }
+               sk->sk_state = SMC_APPCLOSEWAIT1;
                break;
        case SMC_ACTIVE:
                sk->sk_state = SMC_APPCLOSEWAIT1;
@@ -405,8 +398,13 @@ wakeup:
        if (old_state != sk->sk_state) {
                sk->sk_state_change(sk);
                if ((sk->sk_state == SMC_CLOSED) &&
-                   (sock_flag(sk, SOCK_DEAD) || !sk->sk_socket))
+                   (sock_flag(sk, SOCK_DEAD) || !sk->sk_socket)) {
                        smc_conn_free(conn);
+                       if (smc->clcsock) {
+                               sock_release(smc->clcsock);
+                               smc->clcsock = NULL;
+                       }
+               }
        }
        release_sock(sk);
        sock_put(sk); /* sock_hold done by schedulers of close_work */
index 35c1cdc..53a17cf 100644 (file)
@@ -118,7 +118,6 @@ static void __smc_lgr_unregister_conn(struct smc_connection *conn)
        rb_erase(&conn->alert_node, &lgr->conns_all);
        lgr->conns_num--;
        conn->alert_token_local = 0;
-       conn->lgr = NULL;
        sock_put(&smc->sk); /* sock_hold in smc_lgr_register_conn() */
 }
 
@@ -128,6 +127,8 @@ static void smc_lgr_unregister_conn(struct smc_connection *conn)
 {
        struct smc_link_group *lgr = conn->lgr;
 
+       if (!lgr)
+               return;
        write_lock_bh(&lgr->conns_lock);
        if (conn->alert_token_local) {
                __smc_lgr_unregister_conn(conn);
@@ -159,8 +160,6 @@ static void smc_lgr_free_work(struct work_struct *work)
        bool conns;
 
        spin_lock_bh(&smc_lgr_list.lock);
-       if (list_empty(&lgr->list))
-               goto free;
        read_lock_bh(&lgr->conns_lock);
        conns = RB_EMPTY_ROOT(&lgr->conns_all);
        read_unlock_bh(&lgr->conns_lock);
@@ -168,8 +167,8 @@ static void smc_lgr_free_work(struct work_struct *work)
                spin_unlock_bh(&smc_lgr_list.lock);
                return;
        }
-       list_del_init(&lgr->list); /* remove from smc_lgr_list */
-free:
+       if (!list_empty(&lgr->list))
+               list_del_init(&lgr->list); /* remove from smc_lgr_list */
        spin_unlock_bh(&smc_lgr_list.lock);
 
        if (!lgr->is_smcd && !lgr->terminating) {
@@ -300,13 +299,13 @@ static void smc_buf_unuse(struct smc_connection *conn,
                conn->sndbuf_desc->used = 0;
        if (conn->rmb_desc) {
                if (!conn->rmb_desc->regerr) {
-                       conn->rmb_desc->used = 0;
                        if (!lgr->is_smcd) {
                                /* unregister rmb with peer */
                                smc_llc_do_delete_rkey(
                                                &lgr->lnk[SMC_SINGLE_LINK],
                                                conn->rmb_desc);
                        }
+                       conn->rmb_desc->used = 0;
                } else {
                        /* buf registration failed, reuse not possible */
                        write_lock_bh(&lgr->rmbs_lock);
@@ -331,8 +330,9 @@ void smc_conn_free(struct smc_connection *conn)
        } else {
                smc_cdc_tx_dismiss_slots(conn);
        }
-       smc_lgr_unregister_conn(conn);          /* unsets conn->lgr */
+       smc_lgr_unregister_conn(conn);
        smc_buf_unuse(conn, lgr);               /* allow buffer reuse */
+       conn->lgr = NULL;
 
        if (!lgr->conns_num)
                smc_lgr_schedule_free_work(lgr);
@@ -462,6 +462,7 @@ static void __smc_lgr_terminate(struct smc_link_group *lgr)
                sock_hold(&smc->sk); /* sock_put in close work */
                conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
                __smc_lgr_unregister_conn(conn);
+               conn->lgr = NULL;
                write_unlock_bh(&lgr->conns_lock);
                if (!schedule_work(&conn->close_work))
                        sock_put(&smc->sk);
@@ -628,6 +629,8 @@ int smc_conn_create(struct smc_sock *smc, bool is_smcd, int srv_first_contact,
                        local_contact = SMC_REUSE_CONTACT;
                        conn->lgr = lgr;
                        smc_lgr_register_conn(conn); /* add smc conn to lgr */
+                       if (delayed_work_pending(&lgr->free_work))
+                               cancel_delayed_work(&lgr->free_work);
                        write_unlock_bh(&lgr->conns_lock);
                        break;
                }
index b002879..8806d2a 100644 (file)
@@ -52,6 +52,24 @@ enum smc_wr_reg_state {
        FAILED          /* ib_wr_reg_mr response: failure */
 };
 
+struct smc_rdma_sge {                          /* sges for RDMA writes */
+       struct ib_sge           wr_tx_rdma_sge[SMC_IB_MAX_SEND_SGE];
+};
+
+#define SMC_MAX_RDMA_WRITES    2               /* max. # of RDMA writes per
+                                                * message send
+                                                */
+
+struct smc_rdma_sges {                         /* sges per message send */
+       struct smc_rdma_sge     tx_rdma_sge[SMC_MAX_RDMA_WRITES];
+};
+
+struct smc_rdma_wr {                           /* work requests per message
+                                                * send
+                                                */
+       struct ib_rdma_wr       wr_tx_rdma[SMC_MAX_RDMA_WRITES];
+};
+
 struct smc_link {
        struct smc_ib_device    *smcibdev;      /* ib-device */
        u8                      ibport;         /* port - values 1 | 2 */
@@ -64,6 +82,8 @@ struct smc_link {
        struct smc_wr_buf       *wr_tx_bufs;    /* WR send payload buffers */
        struct ib_send_wr       *wr_tx_ibs;     /* WR send meta data */
        struct ib_sge           *wr_tx_sges;    /* WR send gather meta data */
+       struct smc_rdma_sges    *wr_tx_rdma_sges;/*RDMA WRITE gather meta data*/
+       struct smc_rdma_wr      *wr_tx_rdmas;   /* WR RDMA WRITE */
        struct smc_wr_tx_pend   *wr_tx_pends;   /* WR send waiting for CQE */
        /* above four vectors have wr_tx_cnt elements and use the same index */
        dma_addr_t              wr_tx_dma_addr; /* DMA address of wr_tx_bufs */
index dbf64a9..371b4cf 100644 (file)
@@ -38,6 +38,7 @@ static void smc_diag_msg_common_fill(struct smc_diag_msg *r, struct sock *sk)
 {
        struct smc_sock *smc = smc_sk(sk);
 
+       r->diag_family = sk->sk_family;
        if (!smc->clcsock)
                return;
        r->id.idiag_sport = htons(smc->clcsock->sk->sk_num);
@@ -45,14 +46,12 @@ static void smc_diag_msg_common_fill(struct smc_diag_msg *r, struct sock *sk)
        r->id.idiag_if = smc->clcsock->sk->sk_bound_dev_if;
        sock_diag_save_cookie(sk, r->id.idiag_cookie);
        if (sk->sk_protocol == SMCPROTO_SMC) {
-               r->diag_family = PF_INET;
                memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src));
                memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst));
                r->id.idiag_src[0] = smc->clcsock->sk->sk_rcv_saddr;
                r->id.idiag_dst[0] = smc->clcsock->sk->sk_daddr;
 #if IS_ENABLED(CONFIG_IPV6)
        } else if (sk->sk_protocol == SMCPROTO_SMC6) {
-               r->diag_family = PF_INET6;
                memcpy(&r->id.idiag_src, &smc->clcsock->sk->sk_v6_rcv_saddr,
                       sizeof(smc->clcsock->sk->sk_v6_rcv_saddr));
                memcpy(&r->id.idiag_dst, &smc->clcsock->sk->sk_v6_daddr,
index e519ef2..0b244be 100644 (file)
@@ -257,12 +257,20 @@ static void smc_ib_global_event_handler(struct ib_event_handler *handler,
        smcibdev = container_of(handler, struct smc_ib_device, event_handler);
 
        switch (ibevent->event) {
-       case IB_EVENT_PORT_ERR:
        case IB_EVENT_DEVICE_FATAL:
+               /* terminate all ports on device */
+               for (port_idx = 0; port_idx < SMC_MAX_PORTS; port_idx++)
+                       set_bit(port_idx, &smcibdev->port_event_mask);
+               schedule_work(&smcibdev->port_event_work);
+               break;
+       case IB_EVENT_PORT_ERR:
        case IB_EVENT_PORT_ACTIVE:
+       case IB_EVENT_GID_CHANGE:
                port_idx = ibevent->element.port_num - 1;
-               set_bit(port_idx, &smcibdev->port_event_mask);
-               schedule_work(&smcibdev->port_event_work);
+               if (port_idx < SMC_MAX_PORTS) {
+                       set_bit(port_idx, &smcibdev->port_event_mask);
+                       schedule_work(&smcibdev->port_event_work);
+               }
                break;
        default:
                break;
@@ -289,18 +297,18 @@ int smc_ib_create_protection_domain(struct smc_link *lnk)
 
 static void smc_ib_qp_event_handler(struct ib_event *ibevent, void *priv)
 {
-       struct smc_ib_device *smcibdev =
-               (struct smc_ib_device *)ibevent->device;
+       struct smc_link *lnk = (struct smc_link *)priv;
+       struct smc_ib_device *smcibdev = lnk->smcibdev;
        u8 port_idx;
 
        switch (ibevent->event) {
-       case IB_EVENT_DEVICE_FATAL:
-       case IB_EVENT_GID_CHANGE:
-       case IB_EVENT_PORT_ERR:
+       case IB_EVENT_QP_FATAL:
        case IB_EVENT_QP_ACCESS_ERR:
-               port_idx = ibevent->element.port_num - 1;
-               set_bit(port_idx, &smcibdev->port_event_mask);
-               schedule_work(&smcibdev->port_event_work);
+               port_idx = ibevent->element.qp->port - 1;
+               if (port_idx < SMC_MAX_PORTS) {
+                       set_bit(port_idx, &smcibdev->port_event_mask);
+                       schedule_work(&smcibdev->port_event_work);
+               }
                break;
        default:
                break;
index a6d3623..4fd60c5 100644 (file)
@@ -166,7 +166,8 @@ static int smc_llc_add_pending_send(struct smc_link *link,
 {
        int rc;
 
-       rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, pend);
+       rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, NULL,
+                                    pend);
        if (rc < 0)
                return rc;
        BUILD_BUG_ON_MSG(
index 7cb3e4f..632c310 100644 (file)
@@ -27,7 +27,7 @@
 static struct nla_policy smc_pnet_policy[SMC_PNETID_MAX + 1] = {
        [SMC_PNETID_NAME] = {
                .type = NLA_NUL_STRING,
-               .len = SMC_MAX_PNETID_LEN - 1
+               .len = SMC_MAX_PNETID_LEN
        },
        [SMC_PNETID_ETHNAME] = {
                .type = NLA_NUL_STRING,
index d8366ed..a3bff08 100644 (file)
 #include "smc.h"
 #include "smc_wr.h"
 #include "smc_cdc.h"
+#include "smc_close.h"
 #include "smc_ism.h"
 #include "smc_tx.h"
 
-#define SMC_TX_WORK_DELAY      HZ
+#define SMC_TX_WORK_DELAY      0
 #define SMC_TX_CORK_DELAY      (HZ >> 2)       /* 250 ms */
 
 /***************************** sndbuf producer *******************************/
@@ -165,12 +166,11 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len)
                        conn->local_tx_ctrl.prod_flags.urg_data_pending = 1;
 
                if (!atomic_read(&conn->sndbuf_space) || conn->urg_tx_pend) {
+                       if (send_done)
+                               return send_done;
                        rc = smc_tx_wait(smc, msg->msg_flags);
-                       if (rc) {
-                               if (send_done)
-                                       return send_done;
+                       if (rc)
                                goto out_err;
-                       }
                        continue;
                }
 
@@ -267,27 +267,23 @@ int smcd_tx_ism_write(struct smc_connection *conn, void *data, size_t len,
 
 /* sndbuf consumer: actual data transfer of one target chunk with RDMA write */
 static int smc_tx_rdma_write(struct smc_connection *conn, int peer_rmbe_offset,
-                            int num_sges, struct ib_sge sges[])
+                            int num_sges, struct ib_rdma_wr *rdma_wr)
 {
        struct smc_link_group *lgr = conn->lgr;
-       struct ib_rdma_wr rdma_wr;
        struct smc_link *link;
        int rc;
 
-       memset(&rdma_wr, 0, sizeof(rdma_wr));
        link = &lgr->lnk[SMC_SINGLE_LINK];
-       rdma_wr.wr.wr_id = smc_wr_tx_get_next_wr_id(link);
-       rdma_wr.wr.sg_list = sges;
-       rdma_wr.wr.num_sge = num_sges;
-       rdma_wr.wr.opcode = IB_WR_RDMA_WRITE;
-       rdma_wr.remote_addr =
+       rdma_wr->wr.wr_id = smc_wr_tx_get_next_wr_id(link);
+       rdma_wr->wr.num_sge = num_sges;
+       rdma_wr->remote_addr =
                lgr->rtokens[conn->rtoken_idx][SMC_SINGLE_LINK].dma_addr +
                /* RMBE within RMB */
                conn->tx_off +
                /* offset within RMBE */
                peer_rmbe_offset;
-       rdma_wr.rkey = lgr->rtokens[conn->rtoken_idx][SMC_SINGLE_LINK].rkey;
-       rc = ib_post_send(link->roce_qp, &rdma_wr.wr, NULL);
+       rdma_wr->rkey = lgr->rtokens[conn->rtoken_idx][SMC_SINGLE_LINK].rkey;
+       rc = ib_post_send(link->roce_qp, &rdma_wr->wr, NULL);
        if (rc) {
                conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
                smc_lgr_terminate(lgr);
@@ -314,24 +310,25 @@ static inline void smc_tx_advance_cursors(struct smc_connection *conn,
 /* SMC-R helper for smc_tx_rdma_writes() */
 static int smcr_tx_rdma_writes(struct smc_connection *conn, size_t len,
                               size_t src_off, size_t src_len,
-                              size_t dst_off, size_t dst_len)
+                              size_t dst_off, size_t dst_len,
+                              struct smc_rdma_wr *wr_rdma_buf)
 {
        dma_addr_t dma_addr =
                sg_dma_address(conn->sndbuf_desc->sgt[SMC_SINGLE_LINK].sgl);
-       struct smc_link *link = &conn->lgr->lnk[SMC_SINGLE_LINK];
        int src_len_sum = src_len, dst_len_sum = dst_len;
-       struct ib_sge sges[SMC_IB_MAX_SEND_SGE];
        int sent_count = src_off;
        int srcchunk, dstchunk;
        int num_sges;
        int rc;
 
        for (dstchunk = 0; dstchunk < 2; dstchunk++) {
+               struct ib_sge *sge =
+                       wr_rdma_buf->wr_tx_rdma[dstchunk].wr.sg_list;
+
                num_sges = 0;
                for (srcchunk = 0; srcchunk < 2; srcchunk++) {
-                       sges[srcchunk].addr = dma_addr + src_off;
-                       sges[srcchunk].length = src_len;
-                       sges[srcchunk].lkey = link->roce_pd->local_dma_lkey;
+                       sge[srcchunk].addr = dma_addr + src_off;
+                       sge[srcchunk].length = src_len;
                        num_sges++;
 
                        src_off += src_len;
@@ -344,7 +341,8 @@ static int smcr_tx_rdma_writes(struct smc_connection *conn, size_t len,
                        src_len = dst_len - src_len; /* remainder */
                        src_len_sum += src_len;
                }
-               rc = smc_tx_rdma_write(conn, dst_off, num_sges, sges);
+               rc = smc_tx_rdma_write(conn, dst_off, num_sges,
+                                      &wr_rdma_buf->wr_tx_rdma[dstchunk]);
                if (rc)
                        return rc;
                if (dst_len_sum == len)
@@ -403,7 +401,8 @@ static int smcd_tx_rdma_writes(struct smc_connection *conn, size_t len,
 /* sndbuf consumer: prepare all necessary (src&dst) chunks of data transmit;
  * usable snd_wnd as max transmit
  */
-static int smc_tx_rdma_writes(struct smc_connection *conn)
+static int smc_tx_rdma_writes(struct smc_connection *conn,
+                             struct smc_rdma_wr *wr_rdma_buf)
 {
        size_t len, src_len, dst_off, dst_len; /* current chunk values */
        union smc_host_cursor sent, prep, prod, cons;
@@ -464,7 +463,7 @@ static int smc_tx_rdma_writes(struct smc_connection *conn)
                                         dst_off, dst_len);
        else
                rc = smcr_tx_rdma_writes(conn, len, sent.count, src_len,
-                                        dst_off, dst_len);
+                                        dst_off, dst_len, wr_rdma_buf);
        if (rc)
                return rc;
 
@@ -485,31 +484,30 @@ static int smc_tx_rdma_writes(struct smc_connection *conn)
 static int smcr_tx_sndbuf_nonempty(struct smc_connection *conn)
 {
        struct smc_cdc_producer_flags *pflags;
+       struct smc_rdma_wr *wr_rdma_buf;
        struct smc_cdc_tx_pend *pend;
        struct smc_wr_buf *wr_buf;
        int rc;
 
-       spin_lock_bh(&conn->send_lock);
-       rc = smc_cdc_get_free_slot(conn, &wr_buf, &pend);
+       rc = smc_cdc_get_free_slot(conn, &wr_buf, &wr_rdma_buf, &pend);
        if (rc < 0) {
                if (rc == -EBUSY) {
                        struct smc_sock *smc =
                                container_of(conn, struct smc_sock, conn);
 
-                       if (smc->sk.sk_err == ECONNABORTED) {
-                               rc = sock_error(&smc->sk);
-                               goto out_unlock;
-                       }
+                       if (smc->sk.sk_err == ECONNABORTED)
+                               return sock_error(&smc->sk);
                        rc = 0;
                        if (conn->alert_token_local) /* connection healthy */
                                mod_delayed_work(system_wq, &conn->tx_work,
                                                 SMC_TX_WORK_DELAY);
                }
-               goto out_unlock;
+               return rc;
        }
 
+       spin_lock_bh(&conn->send_lock);
        if (!conn->local_tx_ctrl.prod_flags.urg_data_present) {
-               rc = smc_tx_rdma_writes(conn);
+               rc = smc_tx_rdma_writes(conn, wr_rdma_buf);
                if (rc) {
                        smc_wr_tx_put_slot(&conn->lgr->lnk[SMC_SINGLE_LINK],
                                           (struct smc_wr_tx_pend_priv *)pend);
@@ -536,7 +534,7 @@ static int smcd_tx_sndbuf_nonempty(struct smc_connection *conn)
 
        spin_lock_bh(&conn->send_lock);
        if (!pflags->urg_data_present)
-               rc = smc_tx_rdma_writes(conn);
+               rc = smc_tx_rdma_writes(conn, NULL);
        if (!rc)
                rc = smcd_cdc_msg_send(conn);
 
@@ -557,6 +555,12 @@ int smc_tx_sndbuf_nonempty(struct smc_connection *conn)
        else
                rc = smcr_tx_sndbuf_nonempty(conn);
 
+       if (!rc) {
+               /* trigger socket release if connection is closing */
+               struct smc_sock *smc = container_of(conn, struct smc_sock,
+                                                   conn);
+               smc_close_wake_tx_prepared(smc);
+       }
        return rc;
 }
 
@@ -598,7 +602,8 @@ void smc_tx_consumer_update(struct smc_connection *conn, bool force)
        if (to_confirm > conn->rmbe_update_limit) {
                smc_curs_copy(&prod, &conn->local_rx_ctrl.prod, conn);
                sender_free = conn->rmb_desc->len -
-                             smc_curs_diff(conn->rmb_desc->len, &prod, &cfed);
+                             smc_curs_diff_large(conn->rmb_desc->len,
+                                                 &cfed, &prod);
        }
 
        if (conn->local_rx_ctrl.prod_flags.cons_curs_upd_req ||
@@ -612,9 +617,6 @@ void smc_tx_consumer_update(struct smc_connection *conn, bool force)
                                              SMC_TX_WORK_DELAY);
                        return;
                }
-               smc_curs_copy(&conn->rx_curs_confirmed,
-                             &conn->local_tx_ctrl.cons, conn);
-               conn->local_rx_ctrl.prod_flags.cons_curs_upd_req = 0;
        }
        if (conn->local_rx_ctrl.prod_flags.write_blocked &&
            !atomic_read(&conn->bytes_to_rcv))
index c269475..253aa75 100644 (file)
@@ -160,6 +160,7 @@ static inline int smc_wr_tx_get_free_slot_index(struct smc_link *link, u32 *idx)
  * @link:              Pointer to smc_link used to later send the message.
  * @handler:           Send completion handler function pointer.
  * @wr_buf:            Out value returns pointer to message buffer.
+ * @wr_rdma_buf:       Out value returns pointer to rdma work request.
  * @wr_pend_priv:      Out value returns pointer serving as handler context.
  *
  * Return: 0 on success, or -errno on error.
@@ -167,6 +168,7 @@ static inline int smc_wr_tx_get_free_slot_index(struct smc_link *link, u32 *idx)
 int smc_wr_tx_get_free_slot(struct smc_link *link,
                            smc_wr_tx_handler handler,
                            struct smc_wr_buf **wr_buf,
+                           struct smc_rdma_wr **wr_rdma_buf,
                            struct smc_wr_tx_pend_priv **wr_pend_priv)
 {
        struct smc_wr_tx_pend *wr_pend;
@@ -204,6 +206,8 @@ int smc_wr_tx_get_free_slot(struct smc_link *link,
        wr_ib = &link->wr_tx_ibs[idx];
        wr_ib->wr_id = wr_id;
        *wr_buf = &link->wr_tx_bufs[idx];
+       if (wr_rdma_buf)
+               *wr_rdma_buf = &link->wr_tx_rdmas[idx];
        *wr_pend_priv = &wr_pend->priv;
        return 0;
 }
@@ -218,10 +222,10 @@ int smc_wr_tx_put_slot(struct smc_link *link,
                u32 idx = pend->idx;
 
                /* clear the full struct smc_wr_tx_pend including .priv */
-               memset(&link->wr_tx_pends[pend->idx], 0,
-                      sizeof(link->wr_tx_pends[pend->idx]));
-               memset(&link->wr_tx_bufs[pend->idx], 0,
-                      sizeof(link->wr_tx_bufs[pend->idx]));
+               memset(&link->wr_tx_pends[idx], 0,
+                      sizeof(link->wr_tx_pends[idx]));
+               memset(&link->wr_tx_bufs[idx], 0,
+                      sizeof(link->wr_tx_bufs[idx]));
                test_and_clear_bit(idx, link->wr_tx_mask);
                return 1;
        }
@@ -465,12 +469,26 @@ static void smc_wr_init_sge(struct smc_link *lnk)
                        lnk->wr_tx_dma_addr + i * SMC_WR_BUF_SIZE;
                lnk->wr_tx_sges[i].length = SMC_WR_TX_SIZE;
                lnk->wr_tx_sges[i].lkey = lnk->roce_pd->local_dma_lkey;
+               lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge[0].lkey =
+                       lnk->roce_pd->local_dma_lkey;
+               lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge[1].lkey =
+                       lnk->roce_pd->local_dma_lkey;
+               lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge[0].lkey =
+                       lnk->roce_pd->local_dma_lkey;
+               lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge[1].lkey =
+                       lnk->roce_pd->local_dma_lkey;
                lnk->wr_tx_ibs[i].next = NULL;
                lnk->wr_tx_ibs[i].sg_list = &lnk->wr_tx_sges[i];
                lnk->wr_tx_ibs[i].num_sge = 1;
                lnk->wr_tx_ibs[i].opcode = IB_WR_SEND;
                lnk->wr_tx_ibs[i].send_flags =
                        IB_SEND_SIGNALED | IB_SEND_SOLICITED;
+               lnk->wr_tx_rdmas[i].wr_tx_rdma[0].wr.opcode = IB_WR_RDMA_WRITE;
+               lnk->wr_tx_rdmas[i].wr_tx_rdma[1].wr.opcode = IB_WR_RDMA_WRITE;
+               lnk->wr_tx_rdmas[i].wr_tx_rdma[0].wr.sg_list =
+                       lnk->wr_tx_rdma_sges[i].tx_rdma_sge[0].wr_tx_rdma_sge;
+               lnk->wr_tx_rdmas[i].wr_tx_rdma[1].wr.sg_list =
+                       lnk->wr_tx_rdma_sges[i].tx_rdma_sge[1].wr_tx_rdma_sge;
        }
        for (i = 0; i < lnk->wr_rx_cnt; i++) {
                lnk->wr_rx_sges[i].addr =
@@ -521,8 +539,12 @@ void smc_wr_free_link_mem(struct smc_link *lnk)
        lnk->wr_tx_mask = NULL;
        kfree(lnk->wr_tx_sges);
        lnk->wr_tx_sges = NULL;
+       kfree(lnk->wr_tx_rdma_sges);
+       lnk->wr_tx_rdma_sges = NULL;
        kfree(lnk->wr_rx_sges);
        lnk->wr_rx_sges = NULL;
+       kfree(lnk->wr_tx_rdmas);
+       lnk->wr_tx_rdmas = NULL;
        kfree(lnk->wr_rx_ibs);
        lnk->wr_rx_ibs = NULL;
        kfree(lnk->wr_tx_ibs);
@@ -552,10 +574,20 @@ int smc_wr_alloc_link_mem(struct smc_link *link)
                                  GFP_KERNEL);
        if (!link->wr_rx_ibs)
                goto no_mem_wr_tx_ibs;
+       link->wr_tx_rdmas = kcalloc(SMC_WR_BUF_CNT,
+                                   sizeof(link->wr_tx_rdmas[0]),
+                                   GFP_KERNEL);
+       if (!link->wr_tx_rdmas)
+               goto no_mem_wr_rx_ibs;
+       link->wr_tx_rdma_sges = kcalloc(SMC_WR_BUF_CNT,
+                                       sizeof(link->wr_tx_rdma_sges[0]),
+                                       GFP_KERNEL);
+       if (!link->wr_tx_rdma_sges)
+               goto no_mem_wr_tx_rdmas;
        link->wr_tx_sges = kcalloc(SMC_WR_BUF_CNT, sizeof(link->wr_tx_sges[0]),
                                   GFP_KERNEL);
        if (!link->wr_tx_sges)
-               goto no_mem_wr_rx_ibs;
+               goto no_mem_wr_tx_rdma_sges;
        link->wr_rx_sges = kcalloc(SMC_WR_BUF_CNT * 3,
                                   sizeof(link->wr_rx_sges[0]),
                                   GFP_KERNEL);
@@ -579,6 +611,10 @@ no_mem_wr_rx_sges:
        kfree(link->wr_rx_sges);
 no_mem_wr_tx_sges:
        kfree(link->wr_tx_sges);
+no_mem_wr_tx_rdma_sges:
+       kfree(link->wr_tx_rdma_sges);
+no_mem_wr_tx_rdmas:
+       kfree(link->wr_tx_rdmas);
 no_mem_wr_rx_ibs:
        kfree(link->wr_rx_ibs);
 no_mem_wr_tx_ibs:
index 1d85bb1..09bf32f 100644 (file)
@@ -85,6 +85,7 @@ void smc_wr_add_dev(struct smc_ib_device *smcibdev);
 
 int smc_wr_tx_get_free_slot(struct smc_link *link, smc_wr_tx_handler handler,
                            struct smc_wr_buf **wr_buf,
+                           struct smc_rdma_wr **wrs,
                            struct smc_wr_tx_pend_priv **wr_pend_priv);
 int smc_wr_tx_put_slot(struct smc_link *link,
                       struct smc_wr_tx_pend_priv *wr_pend_priv);
index e89884e..643a164 100644 (file)
@@ -669,7 +669,7 @@ static bool skb_is_err_queue(const struct sk_buff *skb)
  * before the software timestamp is received, a hardware TX timestamp may be
  * returned only if there is no software TX timestamp. Ignore false software
  * timestamps, which may be made in the __sock_recv_timestamp() call when the
- * option SO_TIMESTAMP(NS) is enabled on the socket, even when the skb has a
+ * option SO_TIMESTAMP_OLD(NS) is enabled on the socket, even when the skb has a
  * hardware timestamp.
  */
 static bool skb_is_swtx_tstamp(const struct sk_buff *skb, int false_tstamp)
@@ -705,7 +705,9 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
        struct sk_buff *skb)
 {
        int need_software_tstamp = sock_flag(sk, SOCK_RCVTSTAMP);
-       struct scm_timestamping tss;
+       int new_tstamp = sock_flag(sk, SOCK_TSTAMP_NEW);
+       struct scm_timestamping_internal tss;
+
        int empty = 1, false_tstamp = 0;
        struct skb_shared_hwtstamps *shhwtstamps =
                skb_hwtstamps(skb);
@@ -719,34 +721,54 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
 
        if (need_software_tstamp) {
                if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
-                       struct timeval tv;
-                       skb_get_timestamp(skb, &tv);
-                       put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP,
-                                sizeof(tv), &tv);
+                       if (new_tstamp) {
+                               struct __kernel_sock_timeval tv;
+
+                               skb_get_new_timestamp(skb, &tv);
+                               put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_NEW,
+                                        sizeof(tv), &tv);
+                       } else {
+                               struct __kernel_old_timeval tv;
+
+                               skb_get_timestamp(skb, &tv);
+                               put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
+                                        sizeof(tv), &tv);
+                       }
                } else {
-                       struct timespec ts;
-                       skb_get_timestampns(skb, &ts);
-                       put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS,
-                                sizeof(ts), &ts);
+                       if (new_tstamp) {
+                               struct __kernel_timespec ts;
+
+                               skb_get_new_timestampns(skb, &ts);
+                               put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_NEW,
+                                        sizeof(ts), &ts);
+                       } else {
+                               struct timespec ts;
+
+                               skb_get_timestampns(skb, &ts);
+                               put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
+                                        sizeof(ts), &ts);
+                       }
                }
        }
 
        memset(&tss, 0, sizeof(tss));
        if ((sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) &&
-           ktime_to_timespec_cond(skb->tstamp, tss.ts + 0))
+           ktime_to_timespec64_cond(skb->tstamp, tss.ts + 0))
                empty = 0;
        if (shhwtstamps &&
            (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) &&
            !skb_is_swtx_tstamp(skb, false_tstamp) &&
-           ktime_to_timespec_cond(shhwtstamps->hwtstamp, tss.ts + 2)) {
+           ktime_to_timespec64_cond(shhwtstamps->hwtstamp, tss.ts + 2)) {
                empty = 0;
                if ((sk->sk_tsflags & SOF_TIMESTAMPING_OPT_PKTINFO) &&
                    !skb_is_err_queue(skb))
                        put_ts_pktinfo(msg, skb);
        }
        if (!empty) {
-               put_cmsg(msg, SOL_SOCKET,
-                        SCM_TIMESTAMPING, sizeof(tss), &tss);
+               if (sock_flag(sk, SOCK_TSTAMP_NEW))
+                       put_cmsg_scm_timestamping64(msg, &tss);
+               else
+                       put_cmsg_scm_timestamping(msg, &tss);
 
                if (skb_is_err_queue(skb) && skb->len &&
                    SKB_EXT_ERR(skb)->opt_stats)
@@ -941,8 +963,7 @@ void dlci_ioctl_set(int (*hook) (unsigned int, void __user *))
 EXPORT_SYMBOL(dlci_ioctl_set);
 
 static long sock_do_ioctl(struct net *net, struct socket *sock,
-                         unsigned int cmd, unsigned long arg,
-                         unsigned int ifreq_size)
+                         unsigned int cmd, unsigned long arg)
 {
        int err;
        void __user *argp = (void __user *)arg;
@@ -968,11 +989,11 @@ static long sock_do_ioctl(struct net *net, struct socket *sock,
        } else {
                struct ifreq ifr;
                bool need_copyout;
-               if (copy_from_user(&ifr, argp, ifreq_size))
+               if (copy_from_user(&ifr, argp, sizeof(struct ifreq)))
                        return -EFAULT;
                err = dev_ioctl(net, cmd, &ifr, &need_copyout);
                if (!err && need_copyout)
-                       if (copy_to_user(argp, &ifr, ifreq_size))
+                       if (copy_to_user(argp, &ifr, sizeof(struct ifreq)))
                                return -EFAULT;
        }
        return err;
@@ -1071,8 +1092,7 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
                        err = open_related_ns(&net->ns, get_net_ns);
                        break;
                default:
-                       err = sock_do_ioctl(net, sock, cmd, arg,
-                                           sizeof(struct ifreq));
+                       err = sock_do_ioctl(net, sock, cmd, arg);
                        break;
                }
        return err;
@@ -2780,8 +2800,7 @@ static int do_siocgstamp(struct net *net, struct socket *sock,
        int err;
 
        set_fs(KERNEL_DS);
-       err = sock_do_ioctl(net, sock, cmd, (unsigned long)&ktv,
-                           sizeof(struct compat_ifreq));
+       err = sock_do_ioctl(net, sock, cmd, (unsigned long)&ktv);
        set_fs(old_fs);
        if (!err)
                err = compat_put_timeval(&ktv, up);
@@ -2797,8 +2816,7 @@ static int do_siocgstampns(struct net *net, struct socket *sock,
        int err;
 
        set_fs(KERNEL_DS);
-       err = sock_do_ioctl(net, sock, cmd, (unsigned long)&kts,
-                           sizeof(struct compat_ifreq));
+       err = sock_do_ioctl(net, sock, cmd, (unsigned long)&kts);
        set_fs(old_fs);
        if (!err)
                err = compat_put_timespec(&kts, up);
@@ -2994,6 +3012,54 @@ static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
        return dev_ioctl(net, cmd, &ifreq, NULL);
 }
 
+static int compat_ifreq_ioctl(struct net *net, struct socket *sock,
+                             unsigned int cmd,
+                             struct compat_ifreq __user *uifr32)
+{
+       struct ifreq __user *uifr;
+       int err;
+
+       /* Handle the fact that while struct ifreq has the same *layout* on
+        * 32/64 for everything but ifreq::ifru_ifmap and ifreq::ifru_data,
+        * which are handled elsewhere, it still has different *size* due to
+        * ifreq::ifru_ifmap (which is 16 bytes on 32 bit, 24 bytes on 64-bit,
+        * resulting in struct ifreq being 32 and 40 bytes respectively).
+        * As a result, if the struct happens to be at the end of a page and
+        * the next page isn't readable/writable, we get a fault. To prevent
+        * that, copy back and forth to the full size.
+        */
+
+       uifr = compat_alloc_user_space(sizeof(*uifr));
+       if (copy_in_user(uifr, uifr32, sizeof(*uifr32)))
+               return -EFAULT;
+
+       err = sock_do_ioctl(net, sock, cmd, (unsigned long)uifr);
+
+       if (!err) {
+               switch (cmd) {
+               case SIOCGIFFLAGS:
+               case SIOCGIFMETRIC:
+               case SIOCGIFMTU:
+               case SIOCGIFMEM:
+               case SIOCGIFHWADDR:
+               case SIOCGIFINDEX:
+               case SIOCGIFADDR:
+               case SIOCGIFBRDADDR:
+               case SIOCGIFDSTADDR:
+               case SIOCGIFNETMASK:
+               case SIOCGIFPFLAGS:
+               case SIOCGIFTXQLEN:
+               case SIOCGMIIPHY:
+               case SIOCGMIIREG:
+               case SIOCGIFNAME:
+                       if (copy_in_user(uifr32, uifr, sizeof(*uifr32)))
+                               err = -EFAULT;
+                       break;
+               }
+       }
+       return err;
+}
+
 static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
                        struct compat_ifreq __user *uifr32)
 {
@@ -3109,8 +3175,7 @@ static int routing_ioctl(struct net *net, struct socket *sock,
        }
 
        set_fs(KERNEL_DS);
-       ret = sock_do_ioctl(net, sock, cmd, (unsigned long) r,
-                           sizeof(struct compat_ifreq));
+       ret = sock_do_ioctl(net, sock, cmd, (unsigned long) r);
        set_fs(old_fs);
 
 out:
@@ -3210,21 +3275,22 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
        case SIOCSIFTXQLEN:
        case SIOCBRADDIF:
        case SIOCBRDELIF:
+       case SIOCGIFNAME:
        case SIOCSIFNAME:
        case SIOCGMIIPHY:
        case SIOCGMIIREG:
        case SIOCSMIIREG:
-       case SIOCSARP:
-       case SIOCGARP:
-       case SIOCDARP:
-       case SIOCATMARK:
        case SIOCBONDENSLAVE:
        case SIOCBONDRELEASE:
        case SIOCBONDSETHWADDR:
        case SIOCBONDCHANGEACTIVE:
-       case SIOCGIFNAME:
-               return sock_do_ioctl(net, sock, cmd, arg,
-                                    sizeof(struct compat_ifreq));
+               return compat_ifreq_ioctl(net, sock, cmd, argp);
+
+       case SIOCSARP:
+       case SIOCGARP:
+       case SIOCDARP:
+       case SIOCATMARK:
+               return sock_do_ioctl(net, sock, cmd, arg);
        }
 
        return -ENOIOCTLCMD;
index cf51b8f..1f20011 100644 (file)
@@ -537,6 +537,99 @@ void svc_rdma_sync_reply_hdr(struct svcxprt_rdma *rdma,
                                      DMA_TO_DEVICE);
 }
 
+/* If the xdr_buf has more elements than the device can
+ * transmit in a single RDMA Send, then the reply will
+ * have to be copied into a bounce buffer.
+ */
+static bool svc_rdma_pull_up_needed(struct svcxprt_rdma *rdma,
+                                   struct xdr_buf *xdr,
+                                   __be32 *wr_lst)
+{
+       int elements;
+
+       /* xdr->head */
+       elements = 1;
+
+       /* xdr->pages */
+       if (!wr_lst) {
+               unsigned int remaining;
+               unsigned long pageoff;
+
+               pageoff = xdr->page_base & ~PAGE_MASK;
+               remaining = xdr->page_len;
+               while (remaining) {
+                       ++elements;
+                       remaining -= min_t(u32, PAGE_SIZE - pageoff,
+                                          remaining);
+                       pageoff = 0;
+               }
+       }
+
+       /* xdr->tail */
+       if (xdr->tail[0].iov_len)
+               ++elements;
+
+       /* assume 1 SGE is needed for the transport header */
+       return elements >= rdma->sc_max_send_sges;
+}
+
+/* The device is not capable of sending the reply directly.
+ * Assemble the elements of @xdr into the transport header
+ * buffer.
+ */
+static int svc_rdma_pull_up_reply_msg(struct svcxprt_rdma *rdma,
+                                     struct svc_rdma_send_ctxt *ctxt,
+                                     struct xdr_buf *xdr, __be32 *wr_lst)
+{
+       unsigned char *dst, *tailbase;
+       unsigned int taillen;
+
+       dst = ctxt->sc_xprt_buf;
+       dst += ctxt->sc_sges[0].length;
+
+       memcpy(dst, xdr->head[0].iov_base, xdr->head[0].iov_len);
+       dst += xdr->head[0].iov_len;
+
+       tailbase = xdr->tail[0].iov_base;
+       taillen = xdr->tail[0].iov_len;
+       if (wr_lst) {
+               u32 xdrpad;
+
+               xdrpad = xdr_padsize(xdr->page_len);
+               if (taillen && xdrpad) {
+                       tailbase += xdrpad;
+                       taillen -= xdrpad;
+               }
+       } else {
+               unsigned int len, remaining;
+               unsigned long pageoff;
+               struct page **ppages;
+
+               ppages = xdr->pages + (xdr->page_base >> PAGE_SHIFT);
+               pageoff = xdr->page_base & ~PAGE_MASK;
+               remaining = xdr->page_len;
+               while (remaining) {
+                       len = min_t(u32, PAGE_SIZE - pageoff, remaining);
+
+                       memcpy(dst, page_address(*ppages), len);
+                       remaining -= len;
+                       dst += len;
+                       pageoff = 0;
+               }
+       }
+
+       if (taillen)
+               memcpy(dst, tailbase, taillen);
+
+       ctxt->sc_sges[0].length += xdr->len;
+       ib_dma_sync_single_for_device(rdma->sc_pd->device,
+                                     ctxt->sc_sges[0].addr,
+                                     ctxt->sc_sges[0].length,
+                                     DMA_TO_DEVICE);
+
+       return 0;
+}
+
 /* svc_rdma_map_reply_msg - Map the buffer holding RPC message
  * @rdma: controlling transport
  * @ctxt: send_ctxt for the Send WR
@@ -559,8 +652,10 @@ int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma,
        u32 xdr_pad;
        int ret;
 
-       if (++ctxt->sc_cur_sge_no >= rdma->sc_max_send_sges)
-               return -EIO;
+       if (svc_rdma_pull_up_needed(rdma, xdr, wr_lst))
+               return svc_rdma_pull_up_reply_msg(rdma, ctxt, xdr, wr_lst);
+
+       ++ctxt->sc_cur_sge_no;
        ret = svc_rdma_dma_map_buf(rdma, ctxt,
                                   xdr->head[0].iov_base,
                                   xdr->head[0].iov_len);
@@ -591,8 +686,7 @@ int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma,
        while (remaining) {
                len = min_t(u32, PAGE_SIZE - page_off, remaining);
 
-               if (++ctxt->sc_cur_sge_no >= rdma->sc_max_send_sges)
-                       return -EIO;
+               ++ctxt->sc_cur_sge_no;
                ret = svc_rdma_dma_map_page(rdma, ctxt, *ppages++,
                                            page_off, len);
                if (ret < 0)
@@ -606,8 +700,7 @@ int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma,
        len = xdr->tail[0].iov_len;
 tail:
        if (len) {
-               if (++ctxt->sc_cur_sge_no >= rdma->sc_max_send_sges)
-                       return -EIO;
+               ++ctxt->sc_cur_sge_no;
                ret = svc_rdma_dma_map_buf(rdma, ctxt, base, len);
                if (ret < 0)
                        return ret;
index 924c17d..57f86c6 100644 (file)
@@ -419,12 +419,9 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
        /* Transport header, head iovec, tail iovec */
        newxprt->sc_max_send_sges = 3;
        /* Add one SGE per page list entry */
-       newxprt->sc_max_send_sges += svcrdma_max_req_size / PAGE_SIZE;
-       if (newxprt->sc_max_send_sges > dev->attrs.max_send_sge) {
-               pr_err("svcrdma: too few Send SGEs available (%d needed)\n",
-                      newxprt->sc_max_send_sges);
-               goto errout;
-       }
+       newxprt->sc_max_send_sges += (svcrdma_max_req_size / PAGE_SIZE) + 1;
+       if (newxprt->sc_max_send_sges > dev->attrs.max_send_sge)
+               newxprt->sc_max_send_sges = dev->attrs.max_send_sge;
        newxprt->sc_max_req_size = svcrdma_max_req_size;
        newxprt->sc_max_requests = svcrdma_max_requests;
        newxprt->sc_max_bc_requests = svcrdma_max_bc_requests;
index cd78253..7e1357d 100644 (file)
@@ -592,26 +592,6 @@ int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
 }
 EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
 
-bool switchdev_port_same_parent_id(struct net_device *a,
-                                  struct net_device *b)
-{
-       struct switchdev_attr a_attr = {
-               .orig_dev = a,
-               .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
-       };
-       struct switchdev_attr b_attr = {
-               .orig_dev = b,
-               .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
-       };
-
-       if (switchdev_port_attr_get(a, &a_attr) ||
-           switchdev_port_attr_get(b, &b_attr))
-               return false;
-
-       return netdev_phys_item_id_same(&a_attr.u.ppid, &b_attr.u.ppid);
-}
-EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id);
-
 static int __switchdev_handle_port_obj_add(struct net_device *dev,
                        struct switchdev_notifier_port_obj_info *port_obj_info,
                        bool (*check_cb)(const struct net_device *dev),
index ac306d1..341ecd7 100644 (file)
@@ -1145,7 +1145,7 @@ static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb,
        default:
                pr_warn("Dropping received illegal msg type\n");
                kfree_skb(skb);
-               return false;
+               return true;
        };
 }
 
@@ -1425,6 +1425,10 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
                l->rcv_unacked = 0;
        } else {
                /* RESET_MSG or ACTIVATE_MSG */
+               if (mtyp == ACTIVATE_MSG) {
+                       msg_set_dest_session_valid(hdr, 1);
+                       msg_set_dest_session(hdr, l->peer_session);
+               }
                msg_set_max_pkt(hdr, l->advertised_mtu);
                strcpy(data, l->if_name);
                msg_set_size(hdr, INT_H_SIZE + TIPC_MAX_IF_NAME);
@@ -1642,6 +1646,17 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
                        rc = tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
                        break;
                }
+
+               /* If this endpoint was re-created while peer was ESTABLISHING
+                * it doesn't know current session number. Force re-synch.
+                */
+               if (mtyp == ACTIVATE_MSG && msg_dest_session_valid(hdr) &&
+                   l->session != msg_dest_session(hdr)) {
+                       if (less(l->session, msg_dest_session(hdr)))
+                               l->session = msg_dest_session(hdr) + 1;
+                       break;
+               }
+
                /* ACTIVATE_MSG serves as PEER_RESET if link is already down */
                if (mtyp == RESET_MSG || !link_is_up(l))
                        rc = tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT);
index a092495..d7e4b8b 100644 (file)
@@ -360,6 +360,28 @@ static inline void msg_set_bcast_ack(struct tipc_msg *m, u16 n)
        msg_set_bits(m, 1, 0, 0xffff, n);
 }
 
+/* Note: reusing bits in word 1 for ACTIVATE_MSG only, to re-synch
+ * link peer session number
+ */
+static inline bool msg_dest_session_valid(struct tipc_msg *m)
+{
+       return msg_bits(m, 1, 16, 0x1);
+}
+
+static inline void msg_set_dest_session_valid(struct tipc_msg *m, bool valid)
+{
+       msg_set_bits(m, 1, 16, 0x1, valid);
+}
+
+static inline u16 msg_dest_session(struct tipc_msg *m)
+{
+       return msg_bits(m, 1, 0, 0xffff);
+}
+
+static inline void msg_set_dest_session(struct tipc_msg *m, u16 n)
+{
+       msg_set_bits(m, 1, 0, 0xffff, n);
+}
 
 /*
  * Word 2
index db2a6c3..2dc4919 100644 (file)
@@ -830,15 +830,16 @@ static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
        tipc_node_write_lock(n);
        if (!tipc_link_is_establishing(l)) {
                __tipc_node_link_down(n, &bearer_id, &xmitq, &maddr);
-               if (delete) {
-                       kfree(l);
-                       le->link = NULL;
-                       n->link_cnt--;
-               }
        } else {
                /* Defuse pending tipc_node_link_up() */
+               tipc_link_reset(l);
                tipc_link_fsm_evt(l, LINK_RESET_EVT);
        }
+       if (delete) {
+               kfree(l);
+               le->link = NULL;
+               n->link_cnt--;
+       }
        trace_tipc_node_link_down(n, true, "node link down or deleted!");
        tipc_node_write_unlock(n);
        if (delete)
index d753e36..7ee9008 100644 (file)
@@ -257,7 +257,8 @@ static int tls_push_record(struct sock *sk,
        tls_fill_prepend(ctx,
                         skb_frag_address(frag),
                         record->len - ctx->tx.prepend_size,
-                        record_type);
+                        record_type,
+                        ctx->crypto_send.info.version);
 
        /* HW doesn't care about the data in the tag, because it fills it. */
        dummy_tag_frag.page = skb_frag_page(frag);
@@ -270,7 +271,7 @@ static int tls_push_record(struct sock *sk,
        spin_unlock_irq(&offload_ctx->lock);
        offload_ctx->open_record = NULL;
        set_bit(TLS_PENDING_CLOSED_RECORD, &ctx->flags);
-       tls_advance_record_sn(sk, &ctx->tx);
+       tls_advance_record_sn(sk, &ctx->tx, ctx->crypto_send.info.version);
 
        for (i = 0; i < record->num_frags; i++) {
                frag = &record->frags[i];
index 450a6db..54c3a75 100644 (file)
@@ -73,7 +73,8 @@ static int tls_enc_record(struct aead_request *aead_req,
        len -= TLS_CIPHER_AES_GCM_128_IV_SIZE;
 
        tls_make_aad(aad, len - TLS_CIPHER_AES_GCM_128_TAG_SIZE,
-                    (char *)&rcd_sn, sizeof(rcd_sn), buf[0]);
+               (char *)&rcd_sn, sizeof(rcd_sn), buf[0],
+               TLS_1_2_VERSION);
 
        memcpy(iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, buf + TLS_HEADER_SIZE,
               TLS_CIPHER_AES_GCM_128_IV_SIZE);
index d36d095..d1c2fd9 100644 (file)
@@ -372,6 +372,30 @@ static int do_tls_getsockopt_tx(struct sock *sk, char __user *optval,
                        rc = -EFAULT;
                break;
        }
+       case TLS_CIPHER_AES_GCM_256: {
+               struct tls12_crypto_info_aes_gcm_256 *
+                 crypto_info_aes_gcm_256 =
+                 container_of(crypto_info,
+                              struct tls12_crypto_info_aes_gcm_256,
+                              info);
+
+               if (len != sizeof(*crypto_info_aes_gcm_256)) {
+                       rc = -EINVAL;
+                       goto out;
+               }
+               lock_sock(sk);
+               memcpy(crypto_info_aes_gcm_256->iv,
+                      ctx->tx.iv + TLS_CIPHER_AES_GCM_256_SALT_SIZE,
+                      TLS_CIPHER_AES_GCM_256_IV_SIZE);
+               memcpy(crypto_info_aes_gcm_256->rec_seq, ctx->tx.rec_seq,
+                      TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE);
+               release_sock(sk);
+               if (copy_to_user(optval,
+                                crypto_info_aes_gcm_256,
+                                sizeof(*crypto_info_aes_gcm_256)))
+                       rc = -EFAULT;
+               break;
+       }
        default:
                rc = -EINVAL;
        }
@@ -412,6 +436,7 @@ static int do_tls_setsockopt_conf(struct sock *sk, char __user *optval,
 {
        struct tls_crypto_info *crypto_info;
        struct tls_context *ctx = tls_get_ctx(sk);
+       size_t optsize;
        int rc = 0;
        int conf;
 
@@ -438,14 +463,19 @@ static int do_tls_setsockopt_conf(struct sock *sk, char __user *optval,
        }
 
        /* check version */
-       if (crypto_info->version != TLS_1_2_VERSION) {
+       if (crypto_info->version != TLS_1_2_VERSION &&
+           crypto_info->version != TLS_1_3_VERSION) {
                rc = -ENOTSUPP;
                goto err_crypto_info;
        }
 
        switch (crypto_info->cipher_type) {
-       case TLS_CIPHER_AES_GCM_128: {
-               if (optlen != sizeof(struct tls12_crypto_info_aes_gcm_128)) {
+       case TLS_CIPHER_AES_GCM_128:
+       case TLS_CIPHER_AES_GCM_256: {
+               optsize = crypto_info->cipher_type == TLS_CIPHER_AES_GCM_128 ?
+                       sizeof(struct tls12_crypto_info_aes_gcm_128) :
+                       sizeof(struct tls12_crypto_info_aes_gcm_256);
+               if (optlen != optsize) {
                        rc = -EINVAL;
                        goto err_crypto_info;
                }
index 86b9527..ae47847 100644 (file)
@@ -120,6 +120,34 @@ static int skb_nsg(struct sk_buff *skb, int offset, int len)
         return __skb_nsg(skb, offset, len, 0);
 }
 
+static int padding_length(struct tls_sw_context_rx *ctx,
+                         struct tls_context *tls_ctx, struct sk_buff *skb)
+{
+       struct strp_msg *rxm = strp_msg(skb);
+       int sub = 0;
+
+       /* Determine zero-padding length */
+       if (tls_ctx->crypto_recv.info.version == TLS_1_3_VERSION) {
+               char content_type = 0;
+               int err;
+               int back = 17;
+
+               while (content_type == 0) {
+                       if (back > rxm->full_len)
+                               return -EBADMSG;
+                       err = skb_copy_bits(skb,
+                                           rxm->offset + rxm->full_len - back,
+                                           &content_type, 1);
+                       if (content_type)
+                               break;
+                       sub++;
+                       back++;
+               }
+               ctx->control = content_type;
+       }
+       return sub;
+}
+
 static void tls_decrypt_done(struct crypto_async_request *req, int err)
 {
        struct aead_request *aead_req = (struct aead_request *)req;
@@ -142,7 +170,7 @@ static void tls_decrypt_done(struct crypto_async_request *req, int err)
                tls_err_abort(skb->sk, err);
        } else {
                struct strp_msg *rxm = strp_msg(skb);
-
+               rxm->full_len -= padding_length(ctx, tls_ctx, skb);
                rxm->offset += tls_ctx->rx.prepend_size;
                rxm->full_len -= tls_ctx->rx.overhead_size;
        }
@@ -185,7 +213,7 @@ static int tls_do_decryption(struct sock *sk,
        int ret;
 
        aead_request_set_tfm(aead_req, ctx->aead_recv);
-       aead_request_set_ad(aead_req, TLS_AAD_SPACE_SIZE);
+       aead_request_set_ad(aead_req, tls_ctx->rx.aad_size);
        aead_request_set_crypt(aead_req, sgin, sgout,
                               data_len + tls_ctx->rx.tag_size,
                               (u8 *)iv_recv);
@@ -289,12 +317,12 @@ static struct tls_rec *tls_get_rec(struct sock *sk)
 
        sg_init_table(rec->sg_aead_in, 2);
        sg_set_buf(&rec->sg_aead_in[0], rec->aad_space,
-                  sizeof(rec->aad_space));
+                  tls_ctx->tx.aad_size);
        sg_unmark_end(&rec->sg_aead_in[1]);
 
        sg_init_table(rec->sg_aead_out, 2);
        sg_set_buf(&rec->sg_aead_out[0], rec->aad_space,
-                  sizeof(rec->aad_space));
+                  tls_ctx->tx.aad_size);
        sg_unmark_end(&rec->sg_aead_out[1]);
 
        return rec;
@@ -447,16 +475,20 @@ static int tls_do_encryption(struct sock *sk,
        struct scatterlist *sge = sk_msg_elem(msg_en, start);
        int rc;
 
+       memcpy(rec->iv_data, tls_ctx->tx.iv, sizeof(rec->iv_data));
+       xor_iv_with_seq(tls_ctx->crypto_send.info.version, rec->iv_data,
+                       tls_ctx->tx.rec_seq);
+
        sge->offset += tls_ctx->tx.prepend_size;
        sge->length -= tls_ctx->tx.prepend_size;
 
        msg_en->sg.curr = start;
 
        aead_request_set_tfm(aead_req, ctx->aead_send);
-       aead_request_set_ad(aead_req, TLS_AAD_SPACE_SIZE);
+       aead_request_set_ad(aead_req, tls_ctx->tx.aad_size);
        aead_request_set_crypt(aead_req, rec->sg_aead_in,
                               rec->sg_aead_out,
-                              data_len, tls_ctx->tx.iv);
+                              data_len, rec->iv_data);
 
        aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
                                  tls_encrypt_done, sk);
@@ -481,7 +513,8 @@ static int tls_do_encryption(struct sock *sk,
 
        /* Unhook the record from context if encryption is not failure */
        ctx->open_rec = NULL;
-       tls_advance_record_sn(sk, &tls_ctx->tx);
+       tls_advance_record_sn(sk, &tls_ctx->tx,
+                             tls_ctx->crypto_send.info.version);
        return rc;
 }
 
@@ -638,7 +671,17 @@ static int tls_push_record(struct sock *sk, int flags,
 
        i = msg_pl->sg.end;
        sk_msg_iter_var_prev(i);
-       sg_mark_end(sk_msg_elem(msg_pl, i));
+
+       rec->content_type = record_type;
+       if (tls_ctx->crypto_send.info.version == TLS_1_3_VERSION) {
+               /* Add content type to end of message.  No padding added */
+               sg_set_buf(&rec->sg_content_type, &rec->content_type, 1);
+               sg_mark_end(&rec->sg_content_type);
+               sg_chain(msg_pl->sg.data, msg_pl->sg.end + 1,
+                        &rec->sg_content_type);
+       } else {
+               sg_mark_end(sk_msg_elem(msg_pl, i));
+       }
 
        i = msg_pl->sg.start;
        sg_chain(rec->sg_aead_in, 2, rec->inplace_crypto ?
@@ -651,18 +694,22 @@ static int tls_push_record(struct sock *sk, int flags,
        i = msg_en->sg.start;
        sg_chain(rec->sg_aead_out, 2, &msg_en->sg.data[i]);
 
-       tls_make_aad(rec->aad_space, msg_pl->sg.size,
+       tls_make_aad(rec->aad_space, msg_pl->sg.size + tls_ctx->tx.tail_size,
                     tls_ctx->tx.rec_seq, tls_ctx->tx.rec_seq_size,
-                    record_type);
+                    record_type,
+                    tls_ctx->crypto_send.info.version);
 
        tls_fill_prepend(tls_ctx,
                         page_address(sg_page(&msg_en->sg.data[i])) +
-                        msg_en->sg.data[i].offset, msg_pl->sg.size,
-                        record_type);
+                        msg_en->sg.data[i].offset,
+                        msg_pl->sg.size + tls_ctx->tx.tail_size,
+                        record_type,
+                        tls_ctx->crypto_send.info.version);
 
        tls_ctx->pending_open_record_frags = false;
 
-       rc = tls_do_encryption(sk, tls_ctx, ctx, req, msg_pl->sg.size, i);
+       rc = tls_do_encryption(sk, tls_ctx, ctx, req,
+                              msg_pl->sg.size + tls_ctx->tx.tail_size, i);
        if (rc < 0) {
                if (rc != -EINPROGRESS) {
                        tls_err_abort(sk, EBADMSG);
@@ -671,6 +718,7 @@ static int tls_push_record(struct sock *sk, int flags,
                                tls_merge_open_record(sk, rec, tmp, orig_end);
                        }
                }
+               ctx->async_capable = 1;
                return rc;
        } else if (split) {
                msg_pl = &tmp->msg_plaintext;
@@ -812,8 +860,7 @@ int tls_sw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
        long timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
        struct tls_context *tls_ctx = tls_get_ctx(sk);
        struct tls_sw_context_tx *ctx = tls_sw_ctx_tx(tls_ctx);
-       struct crypto_tfm *tfm = crypto_aead_tfm(ctx->aead_send);
-       bool async_capable = tfm->__crt_alg->cra_flags & CRYPTO_ALG_ASYNC;
+       bool async_capable = ctx->async_capable;
        unsigned char record_type = TLS_RECORD_TYPE_DATA;
        bool is_kvec = iov_iter_is_kvec(&msg->msg_iter);
        bool eor = !(msg->msg_flags & MSG_MORE);
@@ -1290,7 +1337,8 @@ static int decrypt_internal(struct sock *sk, struct sk_buff *skb,
        u8 *aad, *iv, *mem = NULL;
        struct scatterlist *sgin = NULL;
        struct scatterlist *sgout = NULL;
-       const int data_len = rxm->full_len - tls_ctx->rx.overhead_size;
+       const int data_len = rxm->full_len - tls_ctx->rx.overhead_size +
+               tls_ctx->rx.tail_size;
 
        if (*zc && (out_iov || out_sg)) {
                if (out_iov)
@@ -1315,7 +1363,7 @@ static int decrypt_internal(struct sock *sk, struct sk_buff *skb,
 
        aead_size = sizeof(*aead_req) + crypto_aead_reqsize(ctx->aead_recv);
        mem_size = aead_size + (nsg * sizeof(struct scatterlist));
-       mem_size = mem_size + TLS_AAD_SPACE_SIZE;
+       mem_size = mem_size + tls_ctx->rx.aad_size;
        mem_size = mem_size + crypto_aead_ivsize(ctx->aead_recv);
 
        /* Allocate a single block of memory which contains
@@ -1331,7 +1379,7 @@ static int decrypt_internal(struct sock *sk, struct sk_buff *skb,
        sgin = (struct scatterlist *)(mem + aead_size);
        sgout = sgin + n_sgin;
        aad = (u8 *)(sgout + n_sgout);
-       iv = aad + TLS_AAD_SPACE_SIZE;
+       iv = aad + tls_ctx->rx.aad_size;
 
        /* Prepare IV */
        err = skb_copy_bits(skb, rxm->offset + TLS_HEADER_SIZE,
@@ -1341,16 +1389,24 @@ static int decrypt_internal(struct sock *sk, struct sk_buff *skb,
                kfree(mem);
                return err;
        }
-       memcpy(iv, tls_ctx->rx.iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+       if (tls_ctx->crypto_recv.info.version == TLS_1_3_VERSION)
+               memcpy(iv, tls_ctx->rx.iv, crypto_aead_ivsize(ctx->aead_recv));
+       else
+               memcpy(iv, tls_ctx->rx.iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+
+       xor_iv_with_seq(tls_ctx->crypto_recv.info.version, iv,
+                       tls_ctx->rx.rec_seq);
 
        /* Prepare AAD */
-       tls_make_aad(aad, rxm->full_len - tls_ctx->rx.overhead_size,
+       tls_make_aad(aad, rxm->full_len - tls_ctx->rx.overhead_size +
+                    tls_ctx->rx.tail_size,
                     tls_ctx->rx.rec_seq, tls_ctx->rx.rec_seq_size,
-                    ctx->control);
+                    ctx->control,
+                    tls_ctx->crypto_recv.info.version);
 
        /* Prepare sgin */
        sg_init_table(sgin, n_sgin);
-       sg_set_buf(&sgin[0], aad, TLS_AAD_SPACE_SIZE);
+       sg_set_buf(&sgin[0], aad, tls_ctx->rx.aad_size);
        err = skb_to_sgvec(skb, &sgin[1],
                           rxm->offset + tls_ctx->rx.prepend_size,
                           rxm->full_len - tls_ctx->rx.prepend_size);
@@ -1362,7 +1418,7 @@ static int decrypt_internal(struct sock *sk, struct sk_buff *skb,
        if (n_sgout) {
                if (out_iov) {
                        sg_init_table(sgout, n_sgout);
-                       sg_set_buf(&sgout[0], aad, TLS_AAD_SPACE_SIZE);
+                       sg_set_buf(&sgout[0], aad, tls_ctx->rx.aad_size);
 
                        *chunk = 0;
                        err = tls_setup_from_iter(sk, out_iov, data_len,
@@ -1403,6 +1459,7 @@ static int decrypt_skb_update(struct sock *sk, struct sk_buff *skb,
 {
        struct tls_context *tls_ctx = tls_get_ctx(sk);
        struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx);
+       int version = tls_ctx->crypto_recv.info.version;
        struct strp_msg *rxm = strp_msg(skb);
        int err = 0;
 
@@ -1415,20 +1472,23 @@ static int decrypt_skb_update(struct sock *sk, struct sk_buff *skb,
                err = decrypt_internal(sk, skb, dest, NULL, chunk, zc, async);
                if (err < 0) {
                        if (err == -EINPROGRESS)
-                               tls_advance_record_sn(sk, &tls_ctx->rx);
+                               tls_advance_record_sn(sk, &tls_ctx->rx,
+                                                     version);
 
                        return err;
                }
+
+               rxm->full_len -= padding_length(ctx, tls_ctx, skb);
+
+               rxm->offset += tls_ctx->rx.prepend_size;
+               rxm->full_len -= tls_ctx->rx.overhead_size;
+               tls_advance_record_sn(sk, &tls_ctx->rx, version);
+               ctx->decrypted = true;
+               ctx->saved_data_ready(sk);
        } else {
                *zc = false;
        }
 
-       rxm->offset += tls_ctx->rx.prepend_size;
-       rxm->full_len -= tls_ctx->rx.overhead_size;
-       tls_advance_record_sn(sk, &tls_ctx->rx);
-       ctx->decrypted = true;
-       ctx->saved_data_ready(sk);
-
        return err;
 }
 
@@ -1585,10 +1645,10 @@ int tls_sw_recvmsg(struct sock *sk,
 
        do {
                bool retain_skb = false;
-               bool async = false;
                bool zc = false;
                int to_decrypt;
                int chunk = 0;
+               bool async;
 
                skb = tls_wait_data(sk, psock, flags, timeo, &err);
                if (!skb) {
@@ -1607,6 +1667,29 @@ int tls_sw_recvmsg(struct sock *sk,
 
                rxm = strp_msg(skb);
 
+               to_decrypt = rxm->full_len - tls_ctx->rx.overhead_size;
+
+               if (to_decrypt <= len && !is_kvec && !is_peek &&
+                   ctx->control == TLS_RECORD_TYPE_DATA &&
+                   tls_ctx->crypto_recv.info.version != TLS_1_3_VERSION)
+                       zc = true;
+
+               /* Do not use async mode if record is non-data */
+               if (ctx->control == TLS_RECORD_TYPE_DATA)
+                       async = ctx->async_capable;
+               else
+                       async = false;
+
+               err = decrypt_skb_update(sk, skb, &msg->msg_iter,
+                                        &chunk, &zc, async);
+               if (err < 0 && err != -EINPROGRESS) {
+                       tls_err_abort(sk, EBADMSG);
+                       goto recv_end;
+               }
+
+               if (err == -EINPROGRESS)
+                       num_async++;
+
                if (!cmsg) {
                        int cerr;
 
@@ -1624,40 +1707,25 @@ int tls_sw_recvmsg(struct sock *sk,
                        goto recv_end;
                }
 
-               to_decrypt = rxm->full_len - tls_ctx->rx.overhead_size;
-
-               if (to_decrypt <= len && !is_kvec && !is_peek)
-                       zc = true;
-
-               err = decrypt_skb_update(sk, skb, &msg->msg_iter,
-                                        &chunk, &zc, ctx->async_capable);
-               if (err < 0 && err != -EINPROGRESS) {
-                       tls_err_abort(sk, EBADMSG);
-                       goto recv_end;
-               }
-
-               if (err == -EINPROGRESS) {
-                       async = true;
-                       num_async++;
+               if (async)
                        goto pick_next_record;
-               } else {
-                       if (!zc) {
-                               if (rxm->full_len > len) {
-                                       retain_skb = true;
-                                       chunk = len;
-                               } else {
-                                       chunk = rxm->full_len;
-                               }
 
-                               err = skb_copy_datagram_msg(skb, rxm->offset,
-                                                           msg, chunk);
-                               if (err < 0)
-                                       goto recv_end;
+               if (!zc) {
+                       if (rxm->full_len > len) {
+                               retain_skb = true;
+                               chunk = len;
+                       } else {
+                               chunk = rxm->full_len;
+                       }
 
-                               if (!is_peek) {
-                                       rxm->offset = rxm->offset + chunk;
-                                       rxm->full_len = rxm->full_len - chunk;
-                               }
+                       err = skb_copy_datagram_msg(skb, rxm->offset,
+                                                   msg, chunk);
+                       if (err < 0)
+                               goto recv_end;
+
+                       if (!is_peek) {
+                               rxm->offset = rxm->offset + chunk;
+                               rxm->full_len = rxm->full_len - chunk;
                        }
                }
 
@@ -1757,15 +1825,15 @@ ssize_t tls_sw_splice_read(struct socket *sock,  loff_t *ppos,
        if (!skb)
                goto splice_read_end;
 
-       /* splice does not support reading control messages */
-       if (ctx->control != TLS_RECORD_TYPE_DATA) {
-               err = -ENOTSUPP;
-               goto splice_read_end;
-       }
-
        if (!ctx->decrypted) {
                err = decrypt_skb_update(sk, skb, NULL, &chunk, &zc, false);
 
+               /* splice does not support reading control messages */
+               if (ctx->control != TLS_RECORD_TYPE_DATA) {
+                       err = -ENOTSUPP;
+                       goto splice_read_end;
+               }
+
                if (err < 0) {
                        tls_err_abort(sk, EBADMSG);
                        goto splice_read_end;
@@ -1833,9 +1901,12 @@ static int tls_read_size(struct strparser *strp, struct sk_buff *skb)
 
        data_len = ((header[4] & 0xFF) | (header[3] << 8));
 
-       cipher_overhead = tls_ctx->rx.tag_size + tls_ctx->rx.iv_size;
+       cipher_overhead = tls_ctx->rx.tag_size;
+       if (tls_ctx->crypto_recv.info.version != TLS_1_3_VERSION)
+               cipher_overhead += tls_ctx->rx.iv_size;
 
-       if (data_len > TLS_MAX_PAYLOAD_SIZE + cipher_overhead) {
+       if (data_len > TLS_MAX_PAYLOAD_SIZE + cipher_overhead +
+           tls_ctx->rx.tail_size) {
                ret = -EMSGSIZE;
                goto read_failure;
        }
@@ -1844,12 +1915,12 @@ static int tls_read_size(struct strparser *strp, struct sk_buff *skb)
                goto read_failure;
        }
 
-       if (header[1] != TLS_VERSION_MINOR(tls_ctx->crypto_recv.info.version) ||
-           header[2] != TLS_VERSION_MAJOR(tls_ctx->crypto_recv.info.version)) {
+       /* Note that both TLS1.3 and TLS1.2 use TLS_1_2 version here */
+       if (header[1] != TLS_1_2_VERSION_MINOR ||
+           header[2] != TLS_1_2_VERSION_MAJOR) {
                ret = -EINVAL;
                goto read_failure;
        }
-
 #ifdef CONFIG_TLS_DEVICE
        handle_device_resync(strp->sk, TCP_SKB_CB(skb)->seq + rxm->offset,
                             *(u64*)tls_ctx->rx.rec_seq);
@@ -1901,7 +1972,9 @@ void tls_sw_free_resources_tx(struct sock *sk)
        if (atomic_read(&ctx->encrypt_pending))
                crypto_wait_req(-EINPROGRESS, &ctx->async_wait);
 
+       release_sock(sk);
        cancel_delayed_work_sync(&ctx->tx_work.work);
+       lock_sock(sk);
 
        /* Tx whatever records we can transmit and abandon the rest */
        tls_tx_records(sk, -1);
@@ -1995,6 +2068,7 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
 {
        struct tls_crypto_info *crypto_info;
        struct tls12_crypto_info_aes_gcm_128 *gcm_128_info;
+       struct tls12_crypto_info_aes_gcm_256 *gcm_256_info;
        struct tls_sw_context_tx *sw_ctx_tx = NULL;
        struct tls_sw_context_rx *sw_ctx_rx = NULL;
        struct cipher_context *cctx;
@@ -2002,7 +2076,8 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
        struct strp_callbacks cb;
        u16 nonce_size, tag_size, iv_size, rec_seq_size;
        struct crypto_tfm *tfm;
-       char *iv, *rec_seq;
+       char *iv, *rec_seq, *key, *salt;
+       size_t keysize;
        int rc = 0;
 
        if (!ctx) {
@@ -2063,6 +2138,24 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
                 ((struct tls12_crypto_info_aes_gcm_128 *)crypto_info)->rec_seq;
                gcm_128_info =
                        (struct tls12_crypto_info_aes_gcm_128 *)crypto_info;
+               keysize = TLS_CIPHER_AES_GCM_128_KEY_SIZE;
+               key = gcm_128_info->key;
+               salt = gcm_128_info->salt;
+               break;
+       }
+       case TLS_CIPHER_AES_GCM_256: {
+               nonce_size = TLS_CIPHER_AES_GCM_256_IV_SIZE;
+               tag_size = TLS_CIPHER_AES_GCM_256_TAG_SIZE;
+               iv_size = TLS_CIPHER_AES_GCM_256_IV_SIZE;
+               iv = ((struct tls12_crypto_info_aes_gcm_256 *)crypto_info)->iv;
+               rec_seq_size = TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE;
+               rec_seq =
+                ((struct tls12_crypto_info_aes_gcm_256 *)crypto_info)->rec_seq;
+               gcm_256_info =
+                       (struct tls12_crypto_info_aes_gcm_256 *)crypto_info;
+               keysize = TLS_CIPHER_AES_GCM_256_KEY_SIZE;
+               key = gcm_256_info->key;
+               salt = gcm_256_info->salt;
                break;
        }
        default:
@@ -2076,9 +2169,19 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
                goto free_priv;
        }
 
+       if (crypto_info->version == TLS_1_3_VERSION) {
+               nonce_size = 0;
+               cctx->aad_size = TLS_HEADER_SIZE;
+               cctx->tail_size = 1;
+       } else {
+               cctx->aad_size = TLS_AAD_SPACE_SIZE;
+               cctx->tail_size = 0;
+       }
+
        cctx->prepend_size = TLS_HEADER_SIZE + nonce_size;
        cctx->tag_size = tag_size;
-       cctx->overhead_size = cctx->prepend_size + cctx->tag_size;
+       cctx->overhead_size = cctx->prepend_size + cctx->tag_size +
+               cctx->tail_size;
        cctx->iv_size = iv_size;
        cctx->iv = kmalloc(iv_size + TLS_CIPHER_AES_GCM_128_SALT_SIZE,
                           GFP_KERNEL);
@@ -2086,7 +2189,8 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
                rc = -ENOMEM;
                goto free_priv;
        }
-       memcpy(cctx->iv, gcm_128_info->salt, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+       /* Note: 128 & 256 bit salt are the same size */
+       memcpy(cctx->iv, salt, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
        memcpy(cctx->iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, iv, iv_size);
        cctx->rec_seq_size = rec_seq_size;
        cctx->rec_seq = kmemdup(rec_seq, rec_seq_size, GFP_KERNEL);
@@ -2106,8 +2210,8 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
 
        ctx->push_pending_record = tls_sw_push_pending_record;
 
-       rc = crypto_aead_setkey(*aead, gcm_128_info->key,
-                               TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+       rc = crypto_aead_setkey(*aead, key, keysize);
+
        if (rc)
                goto free_aead;
 
@@ -2117,8 +2221,12 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
 
        if (sw_ctx_rx) {
                tfm = crypto_aead_tfm(sw_ctx_rx->aead_recv);
-               sw_ctx_rx->async_capable =
-                       tfm->__crt_alg->cra_flags & CRYPTO_ALG_ASYNC;
+
+               if (crypto_info->version == TLS_1_3_VERSION)
+                       sw_ctx_rx->async_capable = false;
+               else
+                       sw_ctx_rx->async_capable =
+                               tfm->__crt_alg->cra_flags & CRYPTO_ALG_ASYNC;
 
                /* Set up strparser */
                memset(&cb, 0, sizeof(cb));
index a60df25..d892000 100644 (file)
@@ -1439,7 +1439,7 @@ static int vsock_stream_setsockopt(struct socket *sock,
                break;
 
        case SO_VM_SOCKETS_CONNECT_TIMEOUT: {
-               struct timeval tv;
+               struct __kernel_old_timeval tv;
                COPY_IN(tv);
                if (tv.tv_sec >= 0 && tv.tv_usec < USEC_PER_SEC &&
                    tv.tv_sec < (MAX_SCHEDULE_TIMEOUT / HZ - 1)) {
@@ -1517,7 +1517,7 @@ static int vsock_stream_getsockopt(struct socket *sock,
                break;
 
        case SO_VM_SOCKETS_CONNECT_TIMEOUT: {
-               struct timeval tv;
+               struct __kernel_old_timeval tv;
                tv.tv_sec = vsk->connect_timeout / HZ;
                tv.tv_usec =
                    (vsk->connect_timeout -
index 5d3cce9..15eb5d3 100644 (file)
@@ -75,6 +75,9 @@ static u32 virtio_transport_get_local_cid(void)
 {
        struct virtio_vsock *vsock = virtio_vsock_get();
 
+       if (!vsock)
+               return VMADDR_CID_ANY;
+
        return vsock->guest_cid;
 }
 
@@ -584,10 +587,6 @@ static int virtio_vsock_probe(struct virtio_device *vdev)
 
        virtio_vsock_update_guest_cid(vsock);
 
-       ret = vsock_core_init(&virtio_transport.transport);
-       if (ret < 0)
-               goto out_vqs;
-
        vsock->rx_buf_nr = 0;
        vsock->rx_buf_max_nr = 0;
        atomic_set(&vsock->queued_replies, 0);
@@ -618,8 +617,6 @@ static int virtio_vsock_probe(struct virtio_device *vdev)
        mutex_unlock(&the_virtio_vsock_mutex);
        return 0;
 
-out_vqs:
-       vsock->vdev->config->del_vqs(vsock->vdev);
 out:
        kfree(vsock);
        mutex_unlock(&the_virtio_vsock_mutex);
@@ -637,6 +634,9 @@ static void virtio_vsock_remove(struct virtio_device *vdev)
        flush_work(&vsock->event_work);
        flush_work(&vsock->send_pkt_work);
 
+       /* Reset all connected sockets when the device disappear */
+       vsock_for_each_connected_socket(virtio_vsock_reset_sock);
+
        vdev->config->reset(vdev);
 
        mutex_lock(&vsock->rx_lock);
@@ -669,7 +669,6 @@ static void virtio_vsock_remove(struct virtio_device *vdev)
 
        mutex_lock(&the_virtio_vsock_mutex);
        the_virtio_vsock = NULL;
-       vsock_core_exit();
        mutex_unlock(&the_virtio_vsock_mutex);
 
        vdev->config->del_vqs(vdev);
@@ -702,14 +701,28 @@ static int __init virtio_vsock_init(void)
        virtio_vsock_workqueue = alloc_workqueue("virtio_vsock", 0, 0);
        if (!virtio_vsock_workqueue)
                return -ENOMEM;
+
        ret = register_virtio_driver(&virtio_vsock_driver);
        if (ret)
-               destroy_workqueue(virtio_vsock_workqueue);
+               goto out_wq;
+
+       ret = vsock_core_init(&virtio_transport.transport);
+       if (ret)
+               goto out_vdr;
+
+       return 0;
+
+out_vdr:
+       unregister_virtio_driver(&virtio_vsock_driver);
+out_wq:
+       destroy_workqueue(virtio_vsock_workqueue);
        return ret;
+
 }
 
 static void __exit virtio_vsock_exit(void)
 {
+       vsock_core_exit();
        unregister_virtio_driver(&virtio_vsock_driver);
        destroy_workqueue(virtio_vsock_workqueue);
 }
index c361ce7..c3d5ab0 100644 (file)
@@ -1651,6 +1651,10 @@ static void vmci_transport_cleanup(struct work_struct *work)
 
 static void vmci_transport_destruct(struct vsock_sock *vsk)
 {
+       /* transport can be NULL if we hit a failure at init() time */
+       if (!vmci_trans(vsk))
+               return;
+
        /* Ensure that the detach callback doesn't use the sk/vsk
         * we are about to destruct.
         */
index 882d97b..550ac9d 100644 (file)
@@ -41,6 +41,8 @@ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
                cfg80211_sched_dfs_chan_update(rdev);
        }
 
+       schedule_work(&cfg80211_disconnect_work);
+
        return err;
 }
 
index 623dfe5..b36ad8e 100644 (file)
@@ -1068,6 +1068,8 @@ static void __cfg80211_unregister_wdev(struct wireless_dev *wdev, bool sync)
 
        ASSERT_RTNL();
 
+       flush_work(&wdev->pmsr_free_wk);
+
        nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
 
        list_del_rcu(&wdev->list);
index c5d6f34..f6b4056 100644 (file)
@@ -445,6 +445,8 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev);
 bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
                                u32 center_freq_khz, u32 bw_khz);
 
+extern struct work_struct cfg80211_disconnect_work;
+
 /**
  * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
  * @wiphy: the wiphy to validate against
index 74150ad..e36437a 100644 (file)
@@ -250,7 +250,7 @@ nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {
        [NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION] =
                NLA_POLICY_MAX(NLA_U8, 15),
        [NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST] =
-               NLA_POLICY_MAX(NLA_U8, 15),
+               NLA_POLICY_MAX(NLA_U8, 31),
        [NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES] = { .type = NLA_U8 },
        [NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI] = { .type = NLA_FLAG },
        [NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC] = { .type = NLA_FLAG },
@@ -259,15 +259,13 @@ nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {
 static const struct nla_policy
 nl80211_pmsr_req_data_policy[NL80211_PMSR_TYPE_MAX + 1] = {
        [NL80211_PMSR_TYPE_FTM] =
-               NLA_POLICY_NESTED(NL80211_PMSR_FTM_REQ_ATTR_MAX,
-                                 nl80211_pmsr_ftm_req_attr_policy),
+               NLA_POLICY_NESTED(nl80211_pmsr_ftm_req_attr_policy),
 };
 
 static const struct nla_policy
 nl80211_pmsr_req_attr_policy[NL80211_PMSR_REQ_ATTR_MAX + 1] = {
        [NL80211_PMSR_REQ_ATTR_DATA] =
-               NLA_POLICY_NESTED(NL80211_PMSR_TYPE_MAX,
-                                 nl80211_pmsr_req_data_policy),
+               NLA_POLICY_NESTED(nl80211_pmsr_req_data_policy),
        [NL80211_PMSR_REQ_ATTR_GET_AP_TSF] = { .type = NLA_FLAG },
 };
 
@@ -280,8 +278,7 @@ nl80211_psmr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = {
         */
        [NL80211_PMSR_PEER_ATTR_CHAN] = { .type = NLA_NESTED },
        [NL80211_PMSR_PEER_ATTR_REQ] =
-               NLA_POLICY_NESTED(NL80211_PMSR_REQ_ATTR_MAX,
-                                 nl80211_pmsr_req_attr_policy),
+               NLA_POLICY_NESTED(nl80211_pmsr_req_attr_policy),
        [NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT },
 };
 
@@ -292,8 +289,7 @@ nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = {
        [NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR] = { .type = NLA_REJECT },
        [NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_REJECT },
        [NL80211_PMSR_ATTR_PEERS] =
-               NLA_POLICY_NESTED_ARRAY(NL80211_PMSR_PEER_ATTR_MAX,
-                                       nl80211_psmr_peer_attr_policy),
+               NLA_POLICY_NESTED_ARRAY(nl80211_psmr_peer_attr_policy),
 };
 
 const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
@@ -555,8 +551,8 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        },
        [NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1),
        [NL80211_ATTR_PEER_MEASUREMENTS] =
-               NLA_POLICY_NESTED(NL80211_PMSR_ATTR_MAX,
-                                 nl80211_pmsr_attr_policy),
+               NLA_POLICY_NESTED(nl80211_pmsr_attr_policy),
+       [NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
 };
 
 /* policy for the key attributes */
@@ -2278,6 +2274,15 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                if (nl80211_send_pmsr_capa(rdev, msg))
                        goto nla_put_failure;
 
+               state->split_start++;
+               break;
+       case 15:
+               if (rdev->wiphy.akm_suites &&
+                   nla_put(msg, NL80211_ATTR_AKM_SUITES,
+                           sizeof(u32) * rdev->wiphy.n_akm_suites,
+                           rdev->wiphy.akm_suites))
+                       goto nla_put_failure;
+
                /* done */
                state->split_start = 0;
                break;
@@ -4540,6 +4545,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
 
        nl80211_calculate_ap_params(&params);
 
+       if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT])
+               params.flags |= AP_SETTINGS_EXTERNAL_AUTH_SUPPORT;
+
        wdev_lock(wdev);
        err = rdev_start_ap(rdev, dev, &params);
        if (!err) {
@@ -4851,6 +4859,11 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
        PUT_SINFO(PLID, plid, u16);
        PUT_SINFO(PLINK_STATE, plink_state, u8);
        PUT_SINFO_U64(RX_DURATION, rx_duration);
+       PUT_SINFO_U64(TX_DURATION, tx_duration);
+
+       if (wiphy_ext_feature_isset(&rdev->wiphy,
+                                   NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+               PUT_SINFO(AIRTIME_WEIGHT, airtime_weight, u16);
 
        switch (rdev->wiphy.signal_type) {
        case CFG80211_SIGNAL_TYPE_MBM:
@@ -5470,6 +5483,15 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                        nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]);
        }
 
+       if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT])
+               params.airtime_weight =
+                       nla_get_u16(info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]);
+
+       if (params.airtime_weight &&
+           !wiphy_ext_feature_isset(&rdev->wiphy,
+                                    NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+               return -EOPNOTSUPP;
+
        /* Include parameters for TDLS peer (will check later) */
        err = nl80211_set_station_tdls(info, &params);
        if (err)
@@ -5598,6 +5620,15 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                params.plink_action =
                        nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
 
+       if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT])
+               params.airtime_weight =
+                       nla_get_u16(info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]);
+
+       if (params.airtime_weight &&
+           !wiphy_ext_feature_isset(&rdev->wiphy,
+                                    NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+               return -EOPNOTSUPP;
+
        err = nl80211_parse_sta_channel_info(info, &params);
        if (err)
                return err;
@@ -5803,7 +5834,13 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
                         pinfo->discovery_timeout)) ||
            ((pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) &&
             nla_put_u8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
-                       pinfo->discovery_retries)))
+                       pinfo->discovery_retries)) ||
+           ((pinfo->filled & MPATH_INFO_HOP_COUNT) &&
+            nla_put_u8(msg, NL80211_MPATH_INFO_HOP_COUNT,
+                       pinfo->hop_count)) ||
+           ((pinfo->filled & MPATH_INFO_PATH_CHANGE) &&
+            nla_put_u32(msg, NL80211_MPATH_INFO_PATH_CHANGE,
+                        pinfo->path_change_count)))
                goto nla_put_failure;
 
        nla_nest_end(msg, pinfoattr);
@@ -9857,7 +9894,10 @@ static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
+           !(dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP &&
+             wiphy_ext_feature_isset(&rdev->wiphy,
+                                     NL80211_EXT_FEATURE_AP_PMKSA_CACHING)))
                return -EOPNOTSUPP;
 
        switch (info->genlhdr->cmd) {
@@ -13047,7 +13087,9 @@ static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info)
        if (!rdev->ops->external_auth)
                return -EOPNOTSUPP;
 
-       if (!info->attrs[NL80211_ATTR_SSID])
+       if (!info->attrs[NL80211_ATTR_SSID] &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
                return -EINVAL;
 
        if (!info->attrs[NL80211_ATTR_BSSID])
@@ -13058,18 +13100,24 @@ static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info)
 
        memset(&params, 0, sizeof(params));
 
-       params.ssid.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
-       if (params.ssid.ssid_len == 0 ||
-           params.ssid.ssid_len > IEEE80211_MAX_SSID_LEN)
-               return -EINVAL;
-       memcpy(params.ssid.ssid, nla_data(info->attrs[NL80211_ATTR_SSID]),
-              params.ssid.ssid_len);
+       if (info->attrs[NL80211_ATTR_SSID]) {
+               params.ssid.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+               if (params.ssid.ssid_len == 0 ||
+                   params.ssid.ssid_len > IEEE80211_MAX_SSID_LEN)
+                       return -EINVAL;
+               memcpy(params.ssid.ssid,
+                      nla_data(info->attrs[NL80211_ATTR_SSID]),
+                      params.ssid.ssid_len);
+       }
 
        memcpy(params.bssid, nla_data(info->attrs[NL80211_ATTR_BSSID]),
               ETH_ALEN);
 
        params.status = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
 
+       if (info->attrs[NL80211_ATTR_PMKID])
+               params.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
+
        return rdev_external_auth(rdev, dev, &params);
 }
 
index de92867..0216ab5 100644 (file)
@@ -256,8 +256,7 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
                if (err)
                        goto out_err;
        } else {
-               memcpy(req->mac_addr, nla_data(info->attrs[NL80211_ATTR_MAC]),
-                      ETH_ALEN);
+               memcpy(req->mac_addr, wdev_address(wdev), ETH_ALEN);
                memset(req->mac_addr_mask, 0xff, ETH_ALEN);
        }
 
@@ -272,6 +271,7 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
 
        req->n_peers = count;
        req->cookie = cfg80211_assign_cookie(rdev);
+       req->nl_portid = info->snd_portid;
 
        err = rdev_start_pmsr(rdev, wdev, req);
        if (err)
@@ -530,14 +530,14 @@ free:
 }
 EXPORT_SYMBOL_GPL(cfg80211_pmsr_report);
 
-void cfg80211_pmsr_free_wk(struct work_struct *work)
+static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev)
 {
-       struct wireless_dev *wdev = container_of(work, struct wireless_dev,
-                                                pmsr_free_wk);
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_pmsr_request *req, *tmp;
        LIST_HEAD(free_list);
 
+       lockdep_assert_held(&wdev->mtx);
+
        spin_lock_bh(&wdev->pmsr_lock);
        list_for_each_entry_safe(req, tmp, &wdev->pmsr_list, list) {
                if (req->nl_portid)
@@ -547,14 +547,22 @@ void cfg80211_pmsr_free_wk(struct work_struct *work)
        spin_unlock_bh(&wdev->pmsr_lock);
 
        list_for_each_entry_safe(req, tmp, &free_list, list) {
-               wdev_lock(wdev);
                rdev_abort_pmsr(rdev, wdev, req);
-               wdev_unlock(wdev);
 
                kfree(req);
        }
 }
 
+void cfg80211_pmsr_free_wk(struct work_struct *work)
+{
+       struct wireless_dev *wdev = container_of(work, struct wireless_dev,
+                                                pmsr_free_wk);
+
+       wdev_lock(wdev);
+       cfg80211_pmsr_process_abort(wdev);
+       wdev_unlock(wdev);
+}
+
 void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
 {
        struct cfg80211_pmsr_request *req;
@@ -568,8 +576,8 @@ void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
        spin_unlock_bh(&wdev->pmsr_lock);
 
        if (found)
-               schedule_work(&wdev->pmsr_free_wk);
-       flush_work(&wdev->pmsr_free_wk);
+               cfg80211_pmsr_process_abort(wdev);
+
        WARN_ON(!list_empty(&wdev->pmsr_list));
 }
 
index dd58b99..adfa58f 100644 (file)
@@ -2729,9 +2729,7 @@ static void notify_self_managed_wiphys(struct regulatory_request *request)
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                wiphy = &rdev->wiphy;
                if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
-                   request->initiator == NL80211_REGDOM_SET_BY_USER &&
-                   request->user_reg_hint_type ==
-                               NL80211_USER_REG_HINT_CELL_BASE)
+                   request->initiator == NL80211_REGDOM_SET_BY_USER)
                        reg_call_notifier(wiphy, request);
        }
 }
index f741d83..7d34cb8 100644 (file)
@@ -667,7 +667,7 @@ static void disconnect_work(struct work_struct *work)
        rtnl_unlock();
 }
 
-static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
+DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
 
 
 /*
index cd48cdd..ec30e37 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright 2017      Intel Deutschland GmbH
- * Copyright (C) 2018 Intel Corporation
+ * Copyright (C) 2018-2019 Intel Corporation
  */
 #include <linux/export.h>
 #include <linux/bitops.h>
@@ -19,6 +19,7 @@
 #include <linux/mpls.h>
 #include <linux/gcd.h>
 #include <linux/bitfield.h>
+#include <linux/nospec.h>
 #include "core.h"
 #include "rdev-ops.h"
 
@@ -715,20 +716,25 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
 {
        unsigned int dscp;
        unsigned char vlan_priority;
+       unsigned int ret;
 
        /* skb->priority values from 256->263 are magic values to
         * directly indicate a specific 802.1d priority.  This is used
         * to allow 802.1d priority to be passed directly in from VLAN
         * tags, etc.
         */
-       if (skb->priority >= 256 && skb->priority <= 263)
-               return skb->priority - 256;
+       if (skb->priority >= 256 && skb->priority <= 263) {
+               ret = skb->priority - 256;
+               goto out;
+       }
 
        if (skb_vlan_tag_present(skb)) {
                vlan_priority = (skb_vlan_tag_get(skb) & VLAN_PRIO_MASK)
                        >> VLAN_PRIO_SHIFT;
-               if (vlan_priority > 0)
-                       return vlan_priority;
+               if (vlan_priority > 0) {
+                       ret = vlan_priority;
+                       goto out;
+               }
        }
 
        switch (skb->protocol) {
@@ -747,8 +753,9 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
                if (!mpls)
                        return 0;
 
-               return (ntohl(mpls->entry) & MPLS_LS_TC_MASK)
+               ret = (ntohl(mpls->entry) & MPLS_LS_TC_MASK)
                        >> MPLS_LS_TC_SHIFT;
+               goto out;
        }
        case htons(ETH_P_80221):
                /* 802.21 is always network control traffic */
@@ -761,18 +768,24 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,
                unsigned int i, tmp_dscp = dscp >> 2;
 
                for (i = 0; i < qos_map->num_des; i++) {
-                       if (tmp_dscp == qos_map->dscp_exception[i].dscp)
-                               return qos_map->dscp_exception[i].up;
+                       if (tmp_dscp == qos_map->dscp_exception[i].dscp) {
+                               ret = qos_map->dscp_exception[i].up;
+                               goto out;
+                       }
                }
 
                for (i = 0; i < 8; i++) {
                        if (tmp_dscp >= qos_map->up[i].low &&
-                           tmp_dscp <= qos_map->up[i].high)
-                               return i;
+                           tmp_dscp <= qos_map->up[i].high) {
+                               ret = i;
+                               goto out;
+                       }
                }
        }
 
-       return dscp >> 5;
+       ret = dscp >> 5;
+out:
+       return array_index_nospec(ret, IEEE80211_NUM_TIDS);
 }
 EXPORT_SYMBOL(cfg80211_classify8021d);
 
index 06943d9..d522787 100644 (file)
@@ -1337,6 +1337,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)
                        wstats.qual.qual = sig + 110;
                        break;
                }
+               /* fall through */
        case CFG80211_SIGNAL_TYPE_UNSPEC:
                if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_SIGNAL)) {
                        wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;
@@ -1345,6 +1346,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)
                        wstats.qual.qual = sinfo.signal;
                        break;
                }
+               /* fall through */
        default:
                wstats.qual.updated |= IW_QUAL_LEVEL_INVALID;
                wstats.qual.updated |= IW_QUAL_QUAL_INVALID;
index 5121729..ec3a828 100644 (file)
@@ -352,17 +352,15 @@ static unsigned int x25_new_lci(struct x25_neigh *nb)
        unsigned int lci = 1;
        struct sock *sk;
 
-       read_lock_bh(&x25_list_lock);
-
-       while ((sk = __x25_find_socket(lci, nb)) != NULL) {
+       while ((sk = x25_find_socket(lci, nb)) != NULL) {
                sock_put(sk);
                if (++lci == 4096) {
                        lci = 0;
                        break;
                }
+               cond_resched();
        }
 
-       read_unlock_bh(&x25_list_lock);
        return lci;
 }
 
index 934492b..ba0a404 100644 (file)
@@ -680,16 +680,6 @@ static void xfrm_hash_resize(struct work_struct *work)
        mutex_unlock(&hash_resize_mutex);
 }
 
-static void xfrm_hash_reset_inexact_table(struct net *net)
-{
-       struct xfrm_pol_inexact_bin *b;
-
-       lockdep_assert_held(&net->xfrm.xfrm_policy_lock);
-
-       list_for_each_entry(b, &net->xfrm.inexact_bins, inexact_bins)
-               INIT_HLIST_HEAD(&b->hhead);
-}
-
 /* Make sure *pol can be inserted into fastbin.
  * Useful to check that later insert requests will be sucessful
  * (provided xfrm_policy_lock is held throughout).
@@ -833,13 +823,13 @@ static void xfrm_policy_inexact_list_reinsert(struct net *net,
                                              u16 family)
 {
        unsigned int matched_s, matched_d;
-       struct hlist_node *newpos = NULL;
        struct xfrm_policy *policy, *p;
 
        matched_s = 0;
        matched_d = 0;
 
        list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) {
+               struct hlist_node *newpos = NULL;
                bool matches_s, matches_d;
 
                if (!policy->bydst_reinsert)
@@ -849,16 +839,19 @@ static void xfrm_policy_inexact_list_reinsert(struct net *net,
 
                policy->bydst_reinsert = false;
                hlist_for_each_entry(p, &n->hhead, bydst) {
-                       if (policy->priority >= p->priority)
+                       if (policy->priority > p->priority)
+                               newpos = &p->bydst;
+                       else if (policy->priority == p->priority &&
+                                policy->pos > p->pos)
                                newpos = &p->bydst;
                        else
                                break;
                }
 
                if (newpos)
-                       hlist_add_behind(&policy->bydst, newpos);
+                       hlist_add_behind_rcu(&policy->bydst, newpos);
                else
-                       hlist_add_head(&policy->bydst, &n->hhead);
+                       hlist_add_head_rcu(&policy->bydst, &n->hhead);
 
                /* paranoia checks follow.
                 * Check that the reinserted policy matches at least
@@ -893,12 +886,13 @@ static void xfrm_policy_inexact_node_reinsert(struct net *net,
                                              struct rb_root *new,
                                              u16 family)
 {
-       struct rb_node **p, *parent = NULL;
        struct xfrm_pol_inexact_node *node;
+       struct rb_node **p, *parent;
 
        /* we should not have another subtree here */
        WARN_ON_ONCE(!RB_EMPTY_ROOT(&n->root));
-
+restart:
+       parent = NULL;
        p = &new->rb_node;
        while (*p) {
                u8 prefixlen;
@@ -918,12 +912,11 @@ static void xfrm_policy_inexact_node_reinsert(struct net *net,
                } else {
                        struct xfrm_policy *tmp;
 
-                       hlist_for_each_entry(tmp, &node->hhead, bydst)
-                               tmp->bydst_reinsert = true;
-                       hlist_for_each_entry(tmp, &n->hhead, bydst)
+                       hlist_for_each_entry(tmp, &n->hhead, bydst) {
                                tmp->bydst_reinsert = true;
+                               hlist_del_rcu(&tmp->bydst);
+                       }
 
-                       INIT_HLIST_HEAD(&node->hhead);
                        xfrm_policy_inexact_list_reinsert(net, node, family);
 
                        if (node->prefixlen == n->prefixlen) {
@@ -935,8 +928,7 @@ static void xfrm_policy_inexact_node_reinsert(struct net *net,
                        kfree_rcu(n, rcu);
                        n = node;
                        n->prefixlen = prefixlen;
-                       *p = new->rb_node;
-                       parent = NULL;
+                       goto restart;
                }
        }
 
@@ -965,12 +957,11 @@ static void xfrm_policy_inexact_node_merge(struct net *net,
                                                  family);
        }
 
-       hlist_for_each_entry(tmp, &v->hhead, bydst)
-               tmp->bydst_reinsert = true;
-       hlist_for_each_entry(tmp, &n->hhead, bydst)
+       hlist_for_each_entry(tmp, &v->hhead, bydst) {
                tmp->bydst_reinsert = true;
+               hlist_del_rcu(&tmp->bydst);
+       }
 
-       INIT_HLIST_HEAD(&n->hhead);
        xfrm_policy_inexact_list_reinsert(net, n, family);
 }
 
@@ -1235,6 +1226,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)
        } while (read_seqretry(&net->xfrm.policy_hthresh.lock, seq));
 
        spin_lock_bh(&net->xfrm.xfrm_policy_lock);
+       write_seqcount_begin(&xfrm_policy_hash_generation);
 
        /* make sure that we can insert the indirect policies again before
         * we start with destructive action.
@@ -1278,10 +1270,14 @@ static void xfrm_hash_rebuild(struct work_struct *work)
        }
 
        /* reset the bydst and inexact table in all directions */
-       xfrm_hash_reset_inexact_table(net);
-
        for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
-               INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]);
+               struct hlist_node *n;
+
+               hlist_for_each_entry_safe(policy, n,
+                                         &net->xfrm.policy_inexact[dir],
+                                         bydst_inexact_list)
+                       hlist_del_init(&policy->bydst_inexact_list);
+
                hmask = net->xfrm.policy_bydst[dir].hmask;
                odst = net->xfrm.policy_bydst[dir].table;
                for (i = hmask; i >= 0; i--)
@@ -1313,6 +1309,9 @@ static void xfrm_hash_rebuild(struct work_struct *work)
                newpos = NULL;
                chain = policy_hash_bysel(net, &policy->selector,
                                          policy->family, dir);
+
+               hlist_del_rcu(&policy->bydst);
+
                if (!chain) {
                        void *p = xfrm_policy_inexact_insert(policy, dir, 0);
 
@@ -1334,6 +1333,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)
 
 out_unlock:
        __xfrm_policy_inexact_flush(net);
+       write_seqcount_end(&xfrm_policy_hash_generation);
        spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
 
        mutex_unlock(&hash_resize_mutex);
@@ -2600,7 +2600,10 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
                dst_copy_metrics(dst1, dst);
 
                if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
-                       __u32 mark = xfrm_smark_get(fl->flowi_mark, xfrm[i]);
+                       __u32 mark = 0;
+
+                       if (xfrm[i]->props.smark.v || xfrm[i]->props.smark.m)
+                               mark = xfrm_smark_get(fl->flowi_mark, xfrm[i]);
 
                        family = xfrm[i]->props.family;
                        dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif,
index 277c1c4..c6d26af 100644 (file)
@@ -1488,10 +1488,15 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
                if (!ut[i].family)
                        ut[i].family = family;
 
-               if ((ut[i].mode == XFRM_MODE_TRANSPORT) &&
-                   (ut[i].family != prev_family))
-                       return -EINVAL;
-
+               switch (ut[i].mode) {
+               case XFRM_MODE_TUNNEL:
+               case XFRM_MODE_BEET:
+                       break;
+               default:
+                       if (ut[i].family != prev_family)
+                               return -EINVAL;
+                       break;
+               }
                if (ut[i].mode >= XFRM_MODE_MAX)
                        return -EINVAL;
 
index db1a91d..a0ef7ed 100644 (file)
@@ -87,18 +87,18 @@ test_cgrp2_sock2-objs := bpf_load.o test_cgrp2_sock2.o
 xdp1-objs := xdp1_user.o
 # reuse xdp1 source intentionally
 xdp2-objs := xdp1_user.o
-xdp_router_ipv4-objs := bpf_load.o xdp_router_ipv4_user.o
+xdp_router_ipv4-objs := xdp_router_ipv4_user.o
 test_current_task_under_cgroup-objs := bpf_load.o $(CGROUP_HELPERS) \
                                       test_current_task_under_cgroup_user.o
 trace_event-objs := bpf_load.o trace_event_user.o $(TRACE_HELPERS)
 sampleip-objs := bpf_load.o sampleip_user.o $(TRACE_HELPERS)
 tc_l2_redirect-objs := bpf_load.o tc_l2_redirect_user.o
 lwt_len_hist-objs := bpf_load.o lwt_len_hist_user.o
-xdp_tx_iptunnel-objs := bpf_load.o xdp_tx_iptunnel_user.o
+xdp_tx_iptunnel-objs := xdp_tx_iptunnel_user.o
 test_map_in_map-objs := bpf_load.o test_map_in_map_user.o
 per_socket_stats_example-objs := cookie_uid_helper_example.o
-xdp_redirect-objs := bpf_load.o xdp_redirect_user.o
-xdp_redirect_map-objs := bpf_load.o xdp_redirect_map_user.o
+xdp_redirect-objs := xdp_redirect_user.o
+xdp_redirect_map-objs := xdp_redirect_map_user.o
 xdp_redirect_cpu-objs := bpf_load.o xdp_redirect_cpu_user.o
 xdp_monitor-objs := bpf_load.o xdp_monitor_user.o
 xdp_rxq_info-objs := xdp_rxq_info_user.o
index 8bfda95..6a64e93 100644 (file)
 #include "bpf/libbpf.h"
 
 static int ifindex;
-static __u32 xdp_flags;
+static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+static __u32 prog_id;
 
 static void int_exit(int sig)
 {
-       bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+       __u32 curr_prog_id = 0;
+
+       if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) {
+               printf("bpf_get_link_xdp_id failed\n");
+               exit(1);
+       }
+       if (prog_id == curr_prog_id)
+               bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+       else if (!curr_prog_id)
+               printf("couldn't find a prog id on a given interface\n");
+       else
+               printf("program on interface changed, not removing\n");
        exit(0);
 }
 
@@ -63,7 +75,8 @@ static void usage(const char *prog)
                "usage: %s [OPTS] IFACE\n\n"
                "OPTS:\n"
                "    -S    use skb-mode\n"
-               "    -N    enforce native mode\n",
+               "    -N    enforce native mode\n"
+               "    -F    force loading prog\n",
                prog);
 }
 
@@ -73,11 +86,14 @@ int main(int argc, char **argv)
        struct bpf_prog_load_attr prog_load_attr = {
                .prog_type      = BPF_PROG_TYPE_XDP,
        };
-       const char *optstr = "SN";
+       struct bpf_prog_info info = {};
+       __u32 info_len = sizeof(info);
+       const char *optstr = "FSN";
        int prog_fd, map_fd, opt;
        struct bpf_object *obj;
        struct bpf_map *map;
        char filename[256];
+       int err;
 
        while ((opt = getopt(argc, argv, optstr)) != -1) {
                switch (opt) {
@@ -87,6 +103,9 @@ int main(int argc, char **argv)
                case 'N':
                        xdp_flags |= XDP_FLAGS_DRV_MODE;
                        break;
+               case 'F':
+                       xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
+                       break;
                default:
                        usage(basename(argv[0]));
                        return 1;
@@ -135,6 +154,13 @@ int main(int argc, char **argv)
                return 1;
        }
 
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (err) {
+               printf("can't get prog info - %s\n", strerror(errno));
+               return err;
+       }
+       prog_id = info.id;
+
        poll_stats(map_fd, 2);
 
        return 0;
index 3042ce3..07e1b92 100644 (file)
 #define STATS_INTERVAL_S 2U
 
 static int ifindex = -1;
-static __u32 xdp_flags;
+static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+static __u32 prog_id;
 
 static void int_exit(int sig)
 {
-       if (ifindex > -1)
-               bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+       __u32 curr_prog_id = 0;
+
+       if (ifindex > -1) {
+               if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) {
+                       printf("bpf_get_link_xdp_id failed\n");
+                       exit(1);
+               }
+               if (prog_id == curr_prog_id)
+                       bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+               else if (!curr_prog_id)
+                       printf("couldn't find a prog id on a given iface\n");
+               else
+                       printf("program on interface changed, not removing\n");
+       }
        exit(0);
 }
 
@@ -60,6 +73,7 @@ static void usage(const char *cmd)
        printf("    -T <stop-after-X-seconds> Default: 0 (forever)\n");
        printf("    -S use skb-mode\n");
        printf("    -N enforce native mode\n");
+       printf("    -F force loading prog\n");
        printf("    -h Display this help\n");
 }
 
@@ -70,12 +84,15 @@ int main(int argc, char **argv)
                .prog_type      = BPF_PROG_TYPE_XDP,
        };
        unsigned char opt_flags[256] = {};
+       const char *optstr = "i:T:SNFh";
+       struct bpf_prog_info info = {};
+       __u32 info_len = sizeof(info);
        unsigned int kill_after_s = 0;
-       const char *optstr = "i:T:SNh";
        int i, prog_fd, map_fd, opt;
        struct bpf_object *obj;
        struct bpf_map *map;
        char filename[256];
+       int err;
 
        for (i = 0; i < strlen(optstr); i++)
                if (optstr[i] != 'h' && 'a' <= optstr[i] && optstr[i] <= 'z')
@@ -96,6 +113,9 @@ int main(int argc, char **argv)
                case 'N':
                        xdp_flags |= XDP_FLAGS_DRV_MODE;
                        break;
+               case 'F':
+                       xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
+                       break;
                default:
                        usage(argv[0]);
                        return 1;
@@ -142,9 +162,15 @@ int main(int argc, char **argv)
                return 1;
        }
 
-       poll_stats(map_fd, kill_after_s);
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (err) {
+               printf("can't get prog info - %s\n", strerror(errno));
+               return 1;
+       }
+       prog_id = info.id;
 
-       bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+       poll_stats(map_fd, kill_after_s);
+       int_exit(0);
 
        return 0;
 }
index 2d23054..586b294 100644 (file)
@@ -24,20 +24,26 @@ static const char *__doc__ =
 /* How many xdp_progs are defined in _kern.c */
 #define MAX_PROG 6
 
-/* Wanted to get rid of bpf_load.h and fake-"libbpf.h" (and instead
- * use bpf/libbpf.h), but cannot as (currently) needed for XDP
- * attaching to a device via bpf_set_link_xdp_fd()
- */
 #include <bpf/bpf.h>
-#include "bpf_load.h"
+#include "bpf/libbpf.h"
 
 #include "bpf_util.h"
 
 static int ifindex = -1;
 static char ifname_buf[IF_NAMESIZE];
 static char *ifname;
-
-static __u32 xdp_flags;
+static __u32 prog_id;
+
+static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+static int cpu_map_fd;
+static int rx_cnt_map_fd;
+static int redirect_err_cnt_map_fd;
+static int cpumap_enqueue_cnt_map_fd;
+static int cpumap_kthread_cnt_map_fd;
+static int cpus_available_map_fd;
+static int cpus_count_map_fd;
+static int cpus_iterator_map_fd;
+static int exception_cnt_map_fd;
 
 /* Exit return codes */
 #define EXIT_OK                0
@@ -51,27 +57,50 @@ static const struct option long_options[] = {
        {"help",        no_argument,            NULL, 'h' },
        {"dev",         required_argument,      NULL, 'd' },
        {"skb-mode",    no_argument,            NULL, 'S' },
-       {"debug",       no_argument,            NULL, 'D' },
        {"sec",         required_argument,      NULL, 's' },
-       {"prognum",     required_argument,      NULL, 'p' },
+       {"progname",    required_argument,      NULL, 'p' },
        {"qsize",       required_argument,      NULL, 'q' },
        {"cpu",         required_argument,      NULL, 'c' },
        {"stress-mode", no_argument,            NULL, 'x' },
        {"no-separators", no_argument,          NULL, 'z' },
+       {"force",       no_argument,            NULL, 'F' },
        {0, 0, NULL,  0 }
 };
 
 static void int_exit(int sig)
 {
-       fprintf(stderr,
-               "Interrupted: Removing XDP program on ifindex:%d device:%s\n",
-               ifindex, ifname);
-       if (ifindex > -1)
-               bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+       __u32 curr_prog_id = 0;
+
+       if (ifindex > -1) {
+               if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) {
+                       printf("bpf_get_link_xdp_id failed\n");
+                       exit(EXIT_FAIL);
+               }
+               if (prog_id == curr_prog_id) {
+                       fprintf(stderr,
+                               "Interrupted: Removing XDP program on ifindex:%d device:%s\n",
+                               ifindex, ifname);
+                       bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+               } else if (!curr_prog_id) {
+                       printf("couldn't find a prog id on a given iface\n");
+               } else {
+                       printf("program on interface changed, not removing\n");
+               }
+       }
        exit(EXIT_OK);
 }
 
-static void usage(char *argv[])
+static void print_avail_progs(struct bpf_object *obj)
+{
+       struct bpf_program *pos;
+
+       bpf_object__for_each_program(pos, obj) {
+               if (bpf_program__is_xdp(pos))
+                       printf(" %s\n", bpf_program__title(pos, false));
+       }
+}
+
+static void usage(char *argv[], struct bpf_object *obj)
 {
        int i;
 
@@ -89,6 +118,8 @@ static void usage(char *argv[])
                                long_options[i].val);
                printf("\n");
        }
+       printf("\n Programs to be used for --progname:\n");
+       print_avail_progs(obj);
        printf("\n");
 }
 
@@ -263,7 +294,7 @@ static __u64 calc_errs_pps(struct datarec *r,
 
 static void stats_print(struct stats_record *stats_rec,
                        struct stats_record *stats_prev,
-                       int prog_num)
+                       char *prog_name)
 {
        unsigned int nr_cpus = bpf_num_possible_cpus();
        double pps = 0, drop = 0, err = 0;
@@ -273,7 +304,7 @@ static void stats_print(struct stats_record *stats_rec,
        int i;
 
        /* Header */
-       printf("Running XDP/eBPF prog_num:%d\n", prog_num);
+       printf("Running XDP/eBPF prog_name:%s\n", prog_name);
        printf("%-15s %-7s %-14s %-11s %-9s\n",
               "XDP-cpumap", "CPU:to", "pps", "drop-pps", "extra-info");
 
@@ -424,20 +455,20 @@ static void stats_collect(struct stats_record *rec)
 {
        int fd, i;
 
-       fd = map_fd[1]; /* map: rx_cnt */
+       fd = rx_cnt_map_fd;
        map_collect_percpu(fd, 0, &rec->rx_cnt);
 
-       fd = map_fd[2]; /* map: redirect_err_cnt */
+       fd = redirect_err_cnt_map_fd;
        map_collect_percpu(fd, 1, &rec->redir_err);
 
-       fd = map_fd[3]; /* map: cpumap_enqueue_cnt */
+       fd = cpumap_enqueue_cnt_map_fd;
        for (i = 0; i < MAX_CPUS; i++)
                map_collect_percpu(fd, i, &rec->enq[i]);
 
-       fd = map_fd[4]; /* map: cpumap_kthread_cnt */
+       fd = cpumap_kthread_cnt_map_fd;
        map_collect_percpu(fd, 0, &rec->kthread);
 
-       fd = map_fd[8]; /* map: exception_cnt */
+       fd = exception_cnt_map_fd;
        map_collect_percpu(fd, 0, &rec->exception);
 }
 
@@ -462,7 +493,7 @@ static int create_cpu_entry(__u32 cpu, __u32 queue_size,
        /* Add a CPU entry to cpumap, as this allocate a cpu entry in
         * the kernel for the cpu.
         */
-       ret = bpf_map_update_elem(map_fd[0], &cpu, &queue_size, 0);
+       ret = bpf_map_update_elem(cpu_map_fd, &cpu, &queue_size, 0);
        if (ret) {
                fprintf(stderr, "Create CPU entry failed (err:%d)\n", ret);
                exit(EXIT_FAIL_BPF);
@@ -471,23 +502,22 @@ static int create_cpu_entry(__u32 cpu, __u32 queue_size,
        /* Inform bpf_prog's that a new CPU is available to select
         * from via some control maps.
         */
-       /* map_fd[5] = cpus_available */
-       ret = bpf_map_update_elem(map_fd[5], &avail_idx, &cpu, 0);
+       ret = bpf_map_update_elem(cpus_available_map_fd, &avail_idx, &cpu, 0);
        if (ret) {
                fprintf(stderr, "Add to avail CPUs failed\n");
                exit(EXIT_FAIL_BPF);
        }
 
        /* When not replacing/updating existing entry, bump the count */
-       /* map_fd[6] = cpus_count */
-       ret = bpf_map_lookup_elem(map_fd[6], &key, &curr_cpus_count);
+       ret = bpf_map_lookup_elem(cpus_count_map_fd, &key, &curr_cpus_count);
        if (ret) {
                fprintf(stderr, "Failed reading curr cpus_count\n");
                exit(EXIT_FAIL_BPF);
        }
        if (new) {
                curr_cpus_count++;
-               ret = bpf_map_update_elem(map_fd[6], &key, &curr_cpus_count, 0);
+               ret = bpf_map_update_elem(cpus_count_map_fd, &key,
+                                         &curr_cpus_count, 0);
                if (ret) {
                        fprintf(stderr, "Failed write curr cpus_count\n");
                        exit(EXIT_FAIL_BPF);
@@ -510,8 +540,8 @@ static void mark_cpus_unavailable(void)
        int ret, i;
 
        for (i = 0; i < MAX_CPUS; i++) {
-               /* map_fd[5] = cpus_available */
-               ret = bpf_map_update_elem(map_fd[5], &i, &invalid_cpu, 0);
+               ret = bpf_map_update_elem(cpus_available_map_fd, &i,
+                                         &invalid_cpu, 0);
                if (ret) {
                        fprintf(stderr, "Failed marking CPU unavailable\n");
                        exit(EXIT_FAIL_BPF);
@@ -531,7 +561,7 @@ static void stress_cpumap(void)
        create_cpu_entry(1, 16000, 0, false);
 }
 
-static void stats_poll(int interval, bool use_separators, int prog_num,
+static void stats_poll(int interval, bool use_separators, char *prog_name,
                       bool stress_mode)
 {
        struct stats_record *record, *prev;
@@ -547,7 +577,7 @@ static void stats_poll(int interval, bool use_separators, int prog_num,
        while (1) {
                swap(&prev, &record);
                stats_collect(record);
-               stats_print(record, prev, prog_num);
+               stats_print(record, prev, prog_name);
                sleep(interval);
                if (stress_mode)
                        stress_cpumap();
@@ -557,20 +587,55 @@ static void stats_poll(int interval, bool use_separators, int prog_num,
        free_stats_record(prev);
 }
 
+static int init_map_fds(struct bpf_object *obj)
+{
+       cpu_map_fd = bpf_object__find_map_fd_by_name(obj, "cpu_map");
+       rx_cnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rx_cnt");
+       redirect_err_cnt_map_fd =
+               bpf_object__find_map_fd_by_name(obj, "redirect_err_cnt");
+       cpumap_enqueue_cnt_map_fd =
+               bpf_object__find_map_fd_by_name(obj, "cpumap_enqueue_cnt");
+       cpumap_kthread_cnt_map_fd =
+               bpf_object__find_map_fd_by_name(obj, "cpumap_kthread_cnt");
+       cpus_available_map_fd =
+               bpf_object__find_map_fd_by_name(obj, "cpus_available");
+       cpus_count_map_fd = bpf_object__find_map_fd_by_name(obj, "cpus_count");
+       cpus_iterator_map_fd =
+               bpf_object__find_map_fd_by_name(obj, "cpus_iterator");
+       exception_cnt_map_fd =
+               bpf_object__find_map_fd_by_name(obj, "exception_cnt");
+
+       if (cpu_map_fd < 0 || rx_cnt_map_fd < 0 ||
+           redirect_err_cnt_map_fd < 0 || cpumap_enqueue_cnt_map_fd < 0 ||
+           cpumap_kthread_cnt_map_fd < 0 || cpus_available_map_fd < 0 ||
+           cpus_count_map_fd < 0 || cpus_iterator_map_fd < 0 ||
+           exception_cnt_map_fd < 0)
+               return -ENOENT;
+
+       return 0;
+}
+
 int main(int argc, char **argv)
 {
        struct rlimit r = {10 * 1024 * 1024, RLIM_INFINITY};
+       char *prog_name = "xdp_cpu_map5_lb_hash_ip_pairs";
+       struct bpf_prog_load_attr prog_load_attr = {
+               .prog_type      = BPF_PROG_TYPE_UNSPEC,
+       };
+       struct bpf_prog_info info = {};
+       __u32 info_len = sizeof(info);
        bool use_separators = true;
        bool stress_mode = false;
+       struct bpf_program *prog;
+       struct bpf_object *obj;
        char filename[256];
-       bool debug = false;
        int added_cpus = 0;
        int longindex = 0;
        int interval = 2;
-       int prog_num = 5;
        int add_cpu = -1;
+       int opt, err;
+       int prog_fd;
        __u32 qsize;
-       int opt;
 
        /* Notice: choosing he queue size is very important with the
         * ixgbe driver, because it's driver page recycling trick is
@@ -581,26 +646,29 @@ int main(int argc, char **argv)
        qsize = 128+64;
 
        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+       prog_load_attr.file = filename;
 
        if (setrlimit(RLIMIT_MEMLOCK, &r)) {
                perror("setrlimit(RLIMIT_MEMLOCK)");
                return 1;
        }
 
-       if (load_bpf_file(filename)) {
-               fprintf(stderr, "ERR in load_bpf_file(): %s", bpf_log_buf);
+       if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
                return EXIT_FAIL;
-       }
 
-       if (!prog_fd[0]) {
-               fprintf(stderr, "ERR: load_bpf_file: %s\n", strerror(errno));
+       if (prog_fd < 0) {
+               fprintf(stderr, "ERR: bpf_prog_load_xattr: %s\n",
+                       strerror(errno));
+               return EXIT_FAIL;
+       }
+       if (init_map_fds(obj) < 0) {
+               fprintf(stderr, "bpf_object__find_map_fd_by_name failed\n");
                return EXIT_FAIL;
        }
-
        mark_cpus_unavailable();
 
        /* Parse commands line args */
-       while ((opt = getopt_long(argc, argv, "hSd:",
+       while ((opt = getopt_long(argc, argv, "hSd:s:p:q:c:xzF",
                                  long_options, &longindex)) != -1) {
                switch (opt) {
                case 'd':
@@ -624,9 +692,6 @@ int main(int argc, char **argv)
                case 'S':
                        xdp_flags |= XDP_FLAGS_SKB_MODE;
                        break;
-               case 'D':
-                       debug = true;
-                       break;
                case 'x':
                        stress_mode = true;
                        break;
@@ -635,13 +700,7 @@ int main(int argc, char **argv)
                        break;
                case 'p':
                        /* Selecting eBPF prog to load */
-                       prog_num = atoi(optarg);
-                       if (prog_num < 0 || prog_num >= MAX_PROG) {
-                               fprintf(stderr,
-                                       "--prognum too large err(%d):%s\n",
-                                       errno, strerror(errno));
-                               goto error;
-                       }
+                       prog_name = optarg;
                        break;
                case 'c':
                        /* Add multiple CPUs */
@@ -658,24 +717,27 @@ int main(int argc, char **argv)
                case 'q':
                        qsize = atoi(optarg);
                        break;
+               case 'F':
+                       xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
+                       break;
                case 'h':
                error:
                default:
-                       usage(argv);
+                       usage(argv, obj);
                        return EXIT_FAIL_OPTION;
                }
        }
        /* Required option */
        if (ifindex == -1) {
                fprintf(stderr, "ERR: required option --dev missing\n");
-               usage(argv);
+               usage(argv, obj);
                return EXIT_FAIL_OPTION;
        }
        /* Required option */
        if (add_cpu == -1) {
                fprintf(stderr, "ERR: required option --cpu missing\n");
                fprintf(stderr, " Specify multiple --cpu option to add more\n");
-               usage(argv);
+               usage(argv, obj);
                return EXIT_FAIL_OPTION;
        }
 
@@ -683,16 +745,30 @@ int main(int argc, char **argv)
        signal(SIGINT, int_exit);
        signal(SIGTERM, int_exit);
 
-       if (bpf_set_link_xdp_fd(ifindex, prog_fd[prog_num], xdp_flags) < 0) {
+       prog = bpf_object__find_program_by_title(obj, prog_name);
+       if (!prog) {
+               fprintf(stderr, "bpf_object__find_program_by_title failed\n");
+               return EXIT_FAIL;
+       }
+
+       prog_fd = bpf_program__fd(prog);
+       if (prog_fd < 0) {
+               fprintf(stderr, "bpf_program__fd failed\n");
+               return EXIT_FAIL;
+       }
+
+       if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) {
                fprintf(stderr, "link set xdp fd failed\n");
                return EXIT_FAIL_XDP;
        }
 
-       if (debug) {
-               printf("Debug-mode reading trace pipe (fix #define DEBUG)\n");
-               read_trace_pipe();
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (err) {
+               printf("can't get prog info - %s\n", strerror(errno));
+               return err;
        }
+       prog_id = info.id;
 
-       stats_poll(interval, use_separators, prog_num, stress_mode);
+       stats_poll(interval, use_separators, prog_name, stress_mode);
        return EXIT_OK;
 }
index 4445e76..327226b 100644 (file)
 #include <libgen.h>
 #include <sys/resource.h>
 
-#include "bpf_load.h"
 #include "bpf_util.h"
 #include <bpf/bpf.h>
+#include "bpf/libbpf.h"
 
 static int ifindex_in;
 static int ifindex_out;
 static bool ifindex_out_xdp_dummy_attached = true;
+static __u32 prog_id;
+static __u32 dummy_prog_id;
 
-static __u32 xdp_flags;
+static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+static int rxcnt_map_fd;
 
 static void int_exit(int sig)
 {
-       bpf_set_link_xdp_fd(ifindex_in, -1, xdp_flags);
-       if (ifindex_out_xdp_dummy_attached)
-               bpf_set_link_xdp_fd(ifindex_out, -1, xdp_flags);
+       __u32 curr_prog_id = 0;
+
+       if (bpf_get_link_xdp_id(ifindex_in, &curr_prog_id, xdp_flags)) {
+               printf("bpf_get_link_xdp_id failed\n");
+               exit(1);
+       }
+       if (prog_id == curr_prog_id)
+               bpf_set_link_xdp_fd(ifindex_in, -1, xdp_flags);
+       else if (!curr_prog_id)
+               printf("couldn't find a prog id on iface IN\n");
+       else
+               printf("program on iface IN changed, not removing\n");
+
+       if (ifindex_out_xdp_dummy_attached) {
+               curr_prog_id = 0;
+               if (bpf_get_link_xdp_id(ifindex_out, &curr_prog_id,
+                                       xdp_flags)) {
+                       printf("bpf_get_link_xdp_id failed\n");
+                       exit(1);
+               }
+               if (prog_id == curr_prog_id)
+                       bpf_set_link_xdp_fd(ifindex_out, -1, xdp_flags);
+               else if (!curr_prog_id)
+                       printf("couldn't find a prog id on iface OUT\n");
+               else
+                       printf("program on iface OUT changed, not removing\n");
+       }
        exit(0);
 }
 
@@ -53,7 +80,7 @@ static void poll_stats(int interval, int ifindex)
                int i;
 
                sleep(interval);
-               assert(bpf_map_lookup_elem(map_fd[1], &key, values) == 0);
+               assert(bpf_map_lookup_elem(rxcnt_map_fd, &key, values) == 0);
                for (i = 0; i < nr_cpus; i++)
                        sum += (values[i] - prev[i]);
                if (sum)
@@ -69,16 +96,26 @@ static void usage(const char *prog)
                "usage: %s [OPTS] IFINDEX_IN IFINDEX_OUT\n\n"
                "OPTS:\n"
                "    -S    use skb-mode\n"
-               "    -N    enforce native mode\n",
+               "    -N    enforce native mode\n"
+               "    -F    force loading prog\n",
                prog);
 }
 
 int main(int argc, char **argv)
 {
        struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
-       const char *optstr = "SN";
-       char filename[256];
+       struct bpf_prog_load_attr prog_load_attr = {
+               .prog_type      = BPF_PROG_TYPE_XDP,
+       };
+       struct bpf_program *prog, *dummy_prog;
+       struct bpf_prog_info info = {};
+       __u32 info_len = sizeof(info);
+       int prog_fd, dummy_prog_fd;
+       const char *optstr = "FSN";
+       struct bpf_object *obj;
        int ret, opt, key = 0;
+       char filename[256];
+       int tx_port_map_fd;
 
        while ((opt = getopt(argc, argv, optstr)) != -1) {
                switch (opt) {
@@ -88,6 +125,9 @@ int main(int argc, char **argv)
                case 'N':
                        xdp_flags |= XDP_FLAGS_DRV_MODE;
                        break;
+               case 'F':
+                       xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
+                       break;
                default:
                        usage(basename(argv[0]));
                        return 1;
@@ -109,37 +149,65 @@ int main(int argc, char **argv)
        printf("input: %d output: %d\n", ifindex_in, ifindex_out);
 
        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+       prog_load_attr.file = filename;
+
+       if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
+               return 1;
 
-       if (load_bpf_file(filename)) {
-               printf("%s", bpf_log_buf);
+       prog = bpf_program__next(NULL, obj);
+       dummy_prog = bpf_program__next(prog, obj);
+       if (!prog || !dummy_prog) {
+               printf("finding a prog in obj file failed\n");
+               return 1;
+       }
+       /* bpf_prog_load_xattr gives us the pointer to first prog's fd,
+        * so we're missing only the fd for dummy prog
+        */
+       dummy_prog_fd = bpf_program__fd(dummy_prog);
+       if (prog_fd < 0 || dummy_prog_fd < 0) {
+               printf("bpf_prog_load_xattr: %s\n", strerror(errno));
                return 1;
        }
 
-       if (!prog_fd[0]) {
-               printf("load_bpf_file: %s\n", strerror(errno));
+       tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
+       rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
+       if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+               printf("bpf_object__find_map_fd_by_name failed\n");
                return 1;
        }
 
-       if (bpf_set_link_xdp_fd(ifindex_in, prog_fd[0], xdp_flags) < 0) {
+       if (bpf_set_link_xdp_fd(ifindex_in, prog_fd, xdp_flags) < 0) {
                printf("ERROR: link set xdp fd failed on %d\n", ifindex_in);
                return 1;
        }
 
+       ret = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (ret) {
+               printf("can't get prog info - %s\n", strerror(errno));
+               return ret;
+       }
+       prog_id = info.id;
+
        /* Loading dummy XDP prog on out-device */
-       if (bpf_set_link_xdp_fd(ifindex_out, prog_fd[1],
+       if (bpf_set_link_xdp_fd(ifindex_out, dummy_prog_fd,
                            (xdp_flags | XDP_FLAGS_UPDATE_IF_NOEXIST)) < 0) {
                printf("WARN: link set xdp fd failed on %d\n", ifindex_out);
                ifindex_out_xdp_dummy_attached = false;
        }
 
+       memset(&info, 0, sizeof(info));
+       ret = bpf_obj_get_info_by_fd(dummy_prog_fd, &info, &info_len);
+       if (ret) {
+               printf("can't get prog info - %s\n", strerror(errno));
+               return ret;
+       }
+       dummy_prog_id = info.id;
+
        signal(SIGINT, int_exit);
        signal(SIGTERM, int_exit);
 
-       printf("map[0] (vports) = %i, map[1] (map) = %i, map[2] (count) = %i\n",
-               map_fd[0], map_fd[1], map_fd[2]);
-
        /* populate virtual to physical port map */
-       ret = bpf_map_update_elem(map_fd[0], &key, &ifindex_out, 0);
+       ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
        if (ret) {
                perror("bpf_update_elem");
                goto out;
index 81a69e3..a5d8ad3 100644 (file)
 #include <libgen.h>
 #include <sys/resource.h>
 
-#include "bpf_load.h"
 #include "bpf_util.h"
 #include <bpf/bpf.h>
+#include "bpf/libbpf.h"
 
 static int ifindex_in;
 static int ifindex_out;
 static bool ifindex_out_xdp_dummy_attached = true;
+static __u32 prog_id;
+static __u32 dummy_prog_id;
 
-static __u32 xdp_flags;
+static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+static int rxcnt_map_fd;
 
 static void int_exit(int sig)
 {
-       bpf_set_link_xdp_fd(ifindex_in, -1, xdp_flags);
-       if (ifindex_out_xdp_dummy_attached)
-               bpf_set_link_xdp_fd(ifindex_out, -1, xdp_flags);
+       __u32 curr_prog_id = 0;
+
+       if (bpf_get_link_xdp_id(ifindex_in, &curr_prog_id, xdp_flags)) {
+               printf("bpf_get_link_xdp_id failed\n");
+               exit(1);
+       }
+       if (prog_id == curr_prog_id)
+               bpf_set_link_xdp_fd(ifindex_in, -1, xdp_flags);
+       else if (!curr_prog_id)
+               printf("couldn't find a prog id on iface IN\n");
+       else
+               printf("program on iface IN changed, not removing\n");
+
+       if (ifindex_out_xdp_dummy_attached) {
+               curr_prog_id = 0;
+               if (bpf_get_link_xdp_id(ifindex_out, &curr_prog_id,
+                                       xdp_flags)) {
+                       printf("bpf_get_link_xdp_id failed\n");
+                       exit(1);
+               }
+               if (prog_id == curr_prog_id)
+                       bpf_set_link_xdp_fd(ifindex_out, -1, xdp_flags);
+               else if (!curr_prog_id)
+                       printf("couldn't find a prog id on iface OUT\n");
+               else
+                       printf("program on iface OUT changed, not removing\n");
+       }
        exit(0);
 }
 
@@ -53,7 +80,7 @@ static void poll_stats(int interval, int ifindex)
                int i;
 
                sleep(interval);
-               assert(bpf_map_lookup_elem(map_fd[1], &key, values) == 0);
+               assert(bpf_map_lookup_elem(rxcnt_map_fd, &key, values) == 0);
                for (i = 0; i < nr_cpus; i++)
                        sum += (values[i] - prev[i]);
                if (sum)
@@ -69,7 +96,8 @@ static void usage(const char *prog)
                "usage: %s [OPTS] IFINDEX_IN IFINDEX_OUT\n\n"
                "OPTS:\n"
                "    -S    use skb-mode\n"
-               "    -N    enforce native mode\n",
+               "    -N    enforce native mode\n"
+               "    -F    force loading prog\n",
                prog);
 }
 
@@ -77,9 +105,18 @@ static void usage(const char *prog)
 int main(int argc, char **argv)
 {
        struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
-       const char *optstr = "SN";
+       struct bpf_prog_load_attr prog_load_attr = {
+               .prog_type      = BPF_PROG_TYPE_XDP,
+       };
+       struct bpf_program *prog, *dummy_prog;
+       int prog_fd, tx_port_map_fd, opt;
+       struct bpf_prog_info info = {};
+       __u32 info_len = sizeof(info);
+       const char *optstr = "FSN";
+       struct bpf_object *obj;
        char filename[256];
-       int ret, opt, key = 0;
+       int dummy_prog_fd;
+       int ret, key = 0;
 
        while ((opt = getopt(argc, argv, optstr)) != -1) {
                switch (opt) {
@@ -89,6 +126,9 @@ int main(int argc, char **argv)
                case 'N':
                        xdp_flags |= XDP_FLAGS_DRV_MODE;
                        break;
+               case 'F':
+                       xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
+                       break;
                default:
                        usage(basename(argv[0]));
                        return 1;
@@ -110,34 +150,65 @@ int main(int argc, char **argv)
        printf("input: %d output: %d\n", ifindex_in, ifindex_out);
 
        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+       prog_load_attr.file = filename;
+
+       if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
+               return 1;
 
-       if (load_bpf_file(filename)) {
-               printf("%s", bpf_log_buf);
+       prog = bpf_program__next(NULL, obj);
+       dummy_prog = bpf_program__next(prog, obj);
+       if (!prog || !dummy_prog) {
+               printf("finding a prog in obj file failed\n");
+               return 1;
+       }
+       /* bpf_prog_load_xattr gives us the pointer to first prog's fd,
+        * so we're missing only the fd for dummy prog
+        */
+       dummy_prog_fd = bpf_program__fd(dummy_prog);
+       if (prog_fd < 0 || dummy_prog_fd < 0) {
+               printf("bpf_prog_load_xattr: %s\n", strerror(errno));
                return 1;
        }
 
-       if (!prog_fd[0]) {
-               printf("load_bpf_file: %s\n", strerror(errno));
+       tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
+       rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
+       if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+               printf("bpf_object__find_map_fd_by_name failed\n");
                return 1;
        }
 
-       if (bpf_set_link_xdp_fd(ifindex_in, prog_fd[0], xdp_flags) < 0) {
+       if (bpf_set_link_xdp_fd(ifindex_in, prog_fd, xdp_flags) < 0) {
                printf("ERROR: link set xdp fd failed on %d\n", ifindex_in);
                return 1;
        }
 
+       ret = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (ret) {
+               printf("can't get prog info - %s\n", strerror(errno));
+               return ret;
+       }
+       prog_id = info.id;
+
        /* Loading dummy XDP prog on out-device */
-       if (bpf_set_link_xdp_fd(ifindex_out, prog_fd[1],
+       if (bpf_set_link_xdp_fd(ifindex_out, dummy_prog_fd,
                            (xdp_flags | XDP_FLAGS_UPDATE_IF_NOEXIST)) < 0) {
                printf("WARN: link set xdp fd failed on %d\n", ifindex_out);
                ifindex_out_xdp_dummy_attached = false;
        }
 
+       memset(&info, 0, sizeof(info));
+       ret = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (ret) {
+               printf("can't get prog info - %s\n", strerror(errno));
+               return ret;
+       }
+       dummy_prog_id = info.id;
+
        signal(SIGINT, int_exit);
        signal(SIGTERM, int_exit);
 
        /* bpf redirect port */
-       ret = bpf_map_update_elem(map_fd[0], &key, &ifindex_out, 0);
+       ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
        if (ret) {
                perror("bpf_update_elem");
                goto out;
index b2b4dfa..79fe7bc 100644 (file)
@@ -15,7 +15,6 @@
 #include <string.h>
 #include <sys/socket.h>
 #include <unistd.h>
-#include "bpf_load.h"
 #include <bpf/bpf.h>
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/syscall.h>
 #include "bpf_util.h"
+#include "bpf/libbpf.h"
+#include <sys/resource.h>
+#include <libgen.h>
 
-int sock, sock_arp, flags = 0;
+int sock, sock_arp, flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
 static int total_ifindex;
-int *ifindex_list;
+static int *ifindex_list;
+static __u32 *prog_id_list;
 char buf[8192];
+static int lpm_map_fd;
+static int rxcnt_map_fd;
+static int arp_table_map_fd;
+static int exact_match_map_fd;
+static int tx_port_map_fd;
 
 static int get_route_table(int rtm_family);
 static void int_exit(int sig)
 {
+       __u32 prog_id = 0;
        int i = 0;
 
-       for (i = 0; i < total_ifindex; i++)
-               bpf_set_link_xdp_fd(ifindex_list[i], -1, flags);
+       for (i = 0; i < total_ifindex; i++) {
+               if (bpf_get_link_xdp_id(ifindex_list[i], &prog_id, flags)) {
+                       printf("bpf_get_link_xdp_id on iface %d failed\n",
+                              ifindex_list[i]);
+                       exit(1);
+               }
+               if (prog_id_list[i] == prog_id)
+                       bpf_set_link_xdp_fd(ifindex_list[i], -1, flags);
+               else if (!prog_id)
+                       printf("couldn't find a prog id on iface %d\n",
+                              ifindex_list[i]);
+               else
+                       printf("program on iface %d changed, not removing\n",
+                              ifindex_list[i]);
+               prog_id = 0;
+       }
        exit(0);
 }
 
 static void close_and_exit(int sig)
 {
-       int i = 0;
-
        close(sock);
        close(sock_arp);
 
-       for (i = 0; i < total_ifindex; i++)
-               bpf_set_link_xdp_fd(ifindex_list[i], -1, flags);
-       exit(0);
+       int_exit(0);
 }
 
 /* Get the mac address of the interface given interface name */
@@ -179,14 +198,10 @@ static void read_route(struct nlmsghdr *nh, int nll)
                route.iface_name = alloca(sizeof(char *) * IFNAMSIZ);
                route.iface_name = if_indextoname(route.iface, route.iface_name);
                route.mac = getmac(route.iface_name);
-               if (route.mac == -1) {
-                       int i = 0;
-
-                       for (i = 0; i < total_ifindex; i++)
-                               bpf_set_link_xdp_fd(ifindex_list[i], -1, flags);
-                       exit(0);
-               }
-               assert(bpf_map_update_elem(map_fd[4], &route.iface, &route.iface, 0) == 0);
+               if (route.mac == -1)
+                       int_exit(0);
+               assert(bpf_map_update_elem(tx_port_map_fd,
+                                          &route.iface, &route.iface, 0) == 0);
                if (rtm_family == AF_INET) {
                        struct trie_value {
                                __u8 prefix[4];
@@ -207,11 +222,16 @@ static void read_route(struct nlmsghdr *nh, int nll)
                        direct_entry.arp.dst = 0;
                        if (route.dst_len == 32) {
                                if (nh->nlmsg_type == RTM_DELROUTE) {
-                                       assert(bpf_map_delete_elem(map_fd[3], &route.dst) == 0);
+                                       assert(bpf_map_delete_elem(exact_match_map_fd,
+                                                                  &route.dst) == 0);
                                } else {
-                                       if (bpf_map_lookup_elem(map_fd[2], &route.dst, &direct_entry.arp.mac) == 0)
+                                       if (bpf_map_lookup_elem(arp_table_map_fd,
+                                                               &route.dst,
+                                                               &direct_entry.arp.mac) == 0)
                                                direct_entry.arp.dst = route.dst;
-                                       assert(bpf_map_update_elem(map_fd[3], &route.dst, &direct_entry, 0) == 0);
+                                       assert(bpf_map_update_elem(exact_match_map_fd,
+                                                                  &route.dst,
+                                                                  &direct_entry, 0) == 0);
                                }
                        }
                        for (i = 0; i < 4; i++)
@@ -225,7 +245,7 @@ static void read_route(struct nlmsghdr *nh, int nll)
                               route.gw, route.dst_len,
                               route.metric,
                               route.iface_name);
-                       if (bpf_map_lookup_elem(map_fd[0], prefix_key,
+                       if (bpf_map_lookup_elem(lpm_map_fd, prefix_key,
                                                prefix_value) < 0) {
                                for (i = 0; i < 4; i++)
                                        prefix_value->prefix[i] = prefix_key->data[i];
@@ -234,7 +254,7 @@ static void read_route(struct nlmsghdr *nh, int nll)
                                prefix_value->gw = route.gw;
                                prefix_value->metric = route.metric;
 
-                               assert(bpf_map_update_elem(map_fd[0],
+                               assert(bpf_map_update_elem(lpm_map_fd,
                                                           prefix_key,
                                                           prefix_value, 0
                                                           ) == 0);
@@ -247,7 +267,7 @@ static void read_route(struct nlmsghdr *nh, int nll)
                                               prefix_key->data[2],
                                               prefix_key->data[3],
                                               prefix_key->prefixlen);
-                                       assert(bpf_map_delete_elem(map_fd[0],
+                                       assert(bpf_map_delete_elem(lpm_map_fd,
                                                                   prefix_key
                                                                   ) == 0);
                                        /* Rereading the route table to check if
@@ -275,8 +295,7 @@ static void read_route(struct nlmsghdr *nh, int nll)
                                        prefix_value->ifindex = route.iface;
                                        prefix_value->gw = route.gw;
                                        prefix_value->metric = route.metric;
-                                       assert(bpf_map_update_elem(
-                                                                  map_fd[0],
+                                       assert(bpf_map_update_elem(lpm_map_fd,
                                                                   prefix_key,
                                                                   prefix_value,
                                                                   0) == 0);
@@ -401,7 +420,8 @@ static void read_arp(struct nlmsghdr *nh, int nll)
                arp_entry.mac = atol(mac);
                printf("%x\t\t%llx\n", arp_entry.dst, arp_entry.mac);
                if (ndm_family == AF_INET) {
-                       if (bpf_map_lookup_elem(map_fd[3], &arp_entry.dst,
+                       if (bpf_map_lookup_elem(exact_match_map_fd,
+                                               &arp_entry.dst,
                                                &direct_entry) == 0) {
                                if (nh->nlmsg_type == RTM_DELNEIGH) {
                                        direct_entry.arp.dst = 0;
@@ -410,16 +430,17 @@ static void read_arp(struct nlmsghdr *nh, int nll)
                                        direct_entry.arp.dst = arp_entry.dst;
                                        direct_entry.arp.mac = arp_entry.mac;
                                }
-                               assert(bpf_map_update_elem(map_fd[3],
+                               assert(bpf_map_update_elem(exact_match_map_fd,
                                                           &arp_entry.dst,
                                                           &direct_entry, 0
                                                           ) == 0);
                                memset(&direct_entry, 0, sizeof(direct_entry));
                        }
                        if (nh->nlmsg_type == RTM_DELNEIGH) {
-                               assert(bpf_map_delete_elem(map_fd[2], &arp_entry.dst) == 0);
+                               assert(bpf_map_delete_elem(arp_table_map_fd,
+                                                          &arp_entry.dst) == 0);
                        } else if (nh->nlmsg_type == RTM_NEWNEIGH) {
-                               assert(bpf_map_update_elem(map_fd[2],
+                               assert(bpf_map_update_elem(arp_table_map_fd,
                                                           &arp_entry.dst,
                                                           &arp_entry.mac, 0
                                                           ) == 0);
@@ -553,7 +574,8 @@ static int monitor_route(void)
                for (key = 0; key < nr_keys; key++) {
                        __u64 sum = 0;
 
-                       assert(bpf_map_lookup_elem(map_fd[1], &key, values) == 0);
+                       assert(bpf_map_lookup_elem(rxcnt_map_fd,
+                                                  &key, values) == 0);
                        for (i = 0; i < nr_cpus; i++)
                                sum += (values[i] - prev[key][i]);
                        if (sum)
@@ -594,36 +616,87 @@ cleanup:
        return ret;
 }
 
+static void usage(const char *prog)
+{
+       fprintf(stderr,
+               "%s: %s [OPTS] interface name list\n\n"
+               "OPTS:\n"
+               "    -S    use skb-mode\n"
+               "    -F    force loading prog\n",
+               __func__, prog);
+}
+
 int main(int ac, char **argv)
 {
+       struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
+       struct bpf_prog_load_attr prog_load_attr = {
+               .prog_type      = BPF_PROG_TYPE_XDP,
+       };
+       struct bpf_prog_info info = {};
+       __u32 info_len = sizeof(info);
+       const char *optstr = "SF";
+       struct bpf_object *obj;
        char filename[256];
        char **ifname_list;
-       int i = 1;
+       int prog_fd, opt;
+       int err, i = 1;
 
        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
-       if (ac < 2) {
-               printf("usage: %s [-S] Interface name list\n", argv[0]);
-               return 1;
+       prog_load_attr.file = filename;
+
+       total_ifindex = ac - 1;
+       ifname_list = (argv + 1);
+
+       while ((opt = getopt(ac, argv, optstr)) != -1) {
+               switch (opt) {
+               case 'S':
+                       flags |= XDP_FLAGS_SKB_MODE;
+                       total_ifindex--;
+                       ifname_list++;
+                       break;
+               case 'F':
+                       flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
+                       total_ifindex--;
+                       ifname_list++;
+                       break;
+               default:
+                       usage(basename(argv[0]));
+                       return 1;
+               }
        }
-       if (!strcmp(argv[1], "-S")) {
-               flags = XDP_FLAGS_SKB_MODE;
-               total_ifindex = ac - 2;
-               ifname_list = (argv + 2);
-       } else {
-               flags = 0;
-               total_ifindex = ac - 1;
-               ifname_list = (argv + 1);
+
+       if (optind == ac) {
+               usage(basename(argv[0]));
+               return 1;
        }
-       if (load_bpf_file(filename)) {
-               printf("%s", bpf_log_buf);
+
+       if (setrlimit(RLIMIT_MEMLOCK, &r)) {
+               perror("setrlimit(RLIMIT_MEMLOCK)");
                return 1;
        }
+
+       if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
+               return 1;
+
        printf("\n**************loading bpf file*********************\n\n\n");
-       if (!prog_fd[0]) {
-               printf("load_bpf_file: %s\n", strerror(errno));
+       if (!prog_fd) {
+               printf("bpf_prog_load_xattr: %s\n", strerror(errno));
                return 1;
        }
-       ifindex_list = (int *)malloc(total_ifindex * sizeof(int *));
+
+       lpm_map_fd = bpf_object__find_map_fd_by_name(obj, "lpm_map");
+       rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
+       arp_table_map_fd = bpf_object__find_map_fd_by_name(obj, "arp_table");
+       exact_match_map_fd = bpf_object__find_map_fd_by_name(obj,
+                                                            "exact_match");
+       tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
+       if (lpm_map_fd < 0 || rxcnt_map_fd < 0 || arp_table_map_fd < 0 ||
+           exact_match_map_fd < 0 || tx_port_map_fd < 0) {
+               printf("bpf_object__find_map_fd_by_name failed\n");
+               return 1;
+       }
+
+       ifindex_list = (int *)calloc(total_ifindex, sizeof(int *));
        for (i = 0; i < total_ifindex; i++) {
                ifindex_list[i] = if_nametoindex(ifname_list[i]);
                if (!ifindex_list[i]) {
@@ -632,8 +705,9 @@ int main(int ac, char **argv)
                        return 1;
                }
        }
+       prog_id_list = (__u32 *)calloc(total_ifindex, sizeof(__u32 *));
        for (i = 0; i < total_ifindex; i++) {
-               if (bpf_set_link_xdp_fd(ifindex_list[i], prog_fd[0], flags) < 0) {
+               if (bpf_set_link_xdp_fd(ifindex_list[i], prog_fd, flags) < 0) {
                        printf("link set xdp fd failed\n");
                        int recovery_index = i;
 
@@ -642,6 +716,13 @@ int main(int ac, char **argv)
 
                        return 1;
                }
+               err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+               if (err) {
+                       printf("can't get prog info - %s\n", strerror(errno));
+                       return err;
+               }
+               prog_id_list[i] = info.id;
+               memset(&info, 0, sizeof(info));
                printf("Attached to %d\n", ifindex_list[i]);
        }
        signal(SIGINT, int_exit);
index ef26f88..1210f3b 100644 (file)
@@ -29,8 +29,9 @@ static const char *__doc__ = " XDP RX-queue info extract example\n\n"
 static int ifindex = -1;
 static char ifname_buf[IF_NAMESIZE];
 static char *ifname;
+static __u32 prog_id;
 
-static __u32 xdp_flags;
+static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
 
 static struct bpf_map *stats_global_map;
 static struct bpf_map *rx_queue_index_map;
@@ -52,16 +53,30 @@ static const struct option long_options[] = {
        {"action",      required_argument,      NULL, 'a' },
        {"readmem",     no_argument,            NULL, 'r' },
        {"swapmac",     no_argument,            NULL, 'm' },
+       {"force",       no_argument,            NULL, 'F' },
        {0, 0, NULL,  0 }
 };
 
 static void int_exit(int sig)
 {
-       fprintf(stderr,
-               "Interrupted: Removing XDP program on ifindex:%d device:%s\n",
-               ifindex, ifname);
-       if (ifindex > -1)
-               bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+       __u32 curr_prog_id = 0;
+
+       if (ifindex > -1) {
+               if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) {
+                       printf("bpf_get_link_xdp_id failed\n");
+                       exit(EXIT_FAIL);
+               }
+               if (prog_id == curr_prog_id) {
+                       fprintf(stderr,
+                               "Interrupted: Removing XDP program on ifindex:%d device:%s\n",
+                               ifindex, ifname);
+                       bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+               } else if (!curr_prog_id) {
+                       printf("couldn't find a prog id on a given iface\n");
+               } else {
+                       printf("program on interface changed, not removing\n");
+               }
+       }
        exit(EXIT_OK);
 }
 
@@ -446,6 +461,8 @@ int main(int argc, char **argv)
        struct bpf_prog_load_attr prog_load_attr = {
                .prog_type      = BPF_PROG_TYPE_XDP,
        };
+       struct bpf_prog_info info = {};
+       __u32 info_len = sizeof(info);
        int prog_fd, map_fd, opt, err;
        bool use_separators = true;
        struct config cfg = { 0 };
@@ -487,7 +504,7 @@ int main(int argc, char **argv)
        }
 
        /* Parse commands line args */
-       while ((opt = getopt_long(argc, argv, "hSd:",
+       while ((opt = getopt_long(argc, argv, "FhSrmzd:s:a:",
                                  long_options, &longindex)) != -1) {
                switch (opt) {
                case 'd':
@@ -524,6 +541,9 @@ int main(int argc, char **argv)
                case 'm':
                        cfg_options |= SWAP_MAC;
                        break;
+               case 'F':
+                       xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
+                       break;
                case 'h':
                error:
                default:
@@ -576,6 +596,13 @@ int main(int argc, char **argv)
                return EXIT_FAIL_XDP;
        }
 
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (err) {
+               printf("can't get prog info - %s\n", strerror(errno));
+               return err;
+       }
+       prog_id = info.id;
+
        stats_poll(interval, action, cfg_options);
        return EXIT_OK;
 }
index 8dd87c1..dc66345 100644 (file)
@@ -12,6 +12,9 @@
 #include <signal.h>
 #include <libbpf.h>
 #include <bpf/bpf.h>
+#include <sys/resource.h>
+#include <libgen.h>
+#include <linux/if_link.h>
 
 #include "perf-sys.h"
 #include "trace_helpers.h"
 static int pmu_fds[MAX_CPUS], if_idx;
 static struct perf_event_mmap_page *headers[MAX_CPUS];
 static char *if_name;
+static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+static __u32 prog_id;
 
 static int do_attach(int idx, int fd, const char *name)
 {
+       struct bpf_prog_info info = {};
+       __u32 info_len = sizeof(info);
        int err;
 
-       err = bpf_set_link_xdp_fd(idx, fd, 0);
-       if (err < 0)
+       err = bpf_set_link_xdp_fd(idx, fd, xdp_flags);
+       if (err < 0) {
                printf("ERROR: failed to attach program to %s\n", name);
+               return err;
+       }
+
+       err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
+       if (err) {
+               printf("can't get prog info - %s\n", strerror(errno));
+               return err;
+       }
+       prog_id = info.id;
 
        return err;
 }
 
 static int do_detach(int idx, const char *name)
 {
-       int err;
+       __u32 curr_prog_id = 0;
+       int err = 0;
 
-       err = bpf_set_link_xdp_fd(idx, -1, 0);
-       if (err < 0)
-               printf("ERROR: failed to detach program from %s\n", name);
+       err = bpf_get_link_xdp_id(idx, &curr_prog_id, 0);
+       if (err) {
+               printf("bpf_get_link_xdp_id failed\n");
+               return err;
+       }
+       if (prog_id == curr_prog_id) {
+               err = bpf_set_link_xdp_fd(idx, -1, 0);
+               if (err < 0)
+                       printf("ERROR: failed to detach prog from %s\n", name);
+       } else if (!curr_prog_id) {
+               printf("couldn't find a prog id on a %s\n", name);
+       } else {
+               printf("program on interface changed, not removing\n");
+       }
 
        return err;
 }
@@ -97,20 +125,47 @@ static void sig_handler(int signo)
        exit(0);
 }
 
+static void usage(const char *prog)
+{
+       fprintf(stderr,
+               "%s: %s [OPTS] <ifname|ifindex>\n\n"
+               "OPTS:\n"
+               "    -F    force loading prog\n",
+               __func__, prog);
+}
+
 int main(int argc, char **argv)
 {
+       struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
        struct bpf_prog_load_attr prog_load_attr = {
                .prog_type      = BPF_PROG_TYPE_XDP,
        };
+       const char *optstr = "F";
+       int prog_fd, map_fd, opt;
        struct bpf_object *obj;
        struct bpf_map *map;
-       int prog_fd, map_fd;
        char filename[256];
        int ret, err, i;
        int numcpus;
 
-       if (argc < 2) {
-               printf("Usage: %s <ifname>\n", argv[0]);
+       while ((opt = getopt(argc, argv, optstr)) != -1) {
+               switch (opt) {
+               case 'F':
+                       xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
+                       break;
+               default:
+                       usage(basename(argv[0]));
+                       return 1;
+               }
+       }
+
+       if (optind == argc) {
+               usage(basename(argv[0]));
+               return 1;
+       }
+
+       if (setrlimit(RLIMIT_MEMLOCK, &r)) {
+               perror("setrlimit(RLIMIT_MEMLOCK)");
                return 1;
        }
 
@@ -136,16 +191,16 @@ int main(int argc, char **argv)
        }
        map_fd = bpf_map__fd(map);
 
-       if_idx = if_nametoindex(argv[1]);
+       if_idx = if_nametoindex(argv[optind]);
        if (!if_idx)
-               if_idx = strtoul(argv[1], NULL, 0);
+               if_idx = strtoul(argv[optind], NULL, 0);
 
        if (!if_idx) {
                fprintf(stderr, "Invalid ifname\n");
                return 1;
        }
-       if_name = argv[1];
-       err = do_attach(if_idx, prog_fd, argv[1]);
+       if_name = argv[optind];
+       err = do_attach(if_idx, prog_fd, if_name);
        if (err)
                return err;
 
index a4ccc33..4a1511e 100644 (file)
@@ -17,7 +17,7 @@
 #include <netinet/ether.h>
 #include <unistd.h>
 #include <time.h>
-#include "bpf_load.h"
+#include "bpf/libbpf.h"
 #include <bpf/bpf.h>
 #include "bpf_util.h"
 #include "xdp_tx_iptunnel_common.h"
 #define STATS_INTERVAL_S 2U
 
 static int ifindex = -1;
-static __u32 xdp_flags = 0;
+static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+static int rxcnt_map_fd;
+static __u32 prog_id;
 
 static void int_exit(int sig)
 {
-       if (ifindex > -1)
-               bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+       __u32 curr_prog_id = 0;
+
+       if (ifindex > -1) {
+               if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) {
+                       printf("bpf_get_link_xdp_id failed\n");
+                       exit(1);
+               }
+               if (prog_id == curr_prog_id)
+                       bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+               else if (!curr_prog_id)
+                       printf("couldn't find a prog id on a given iface\n");
+               else
+                       printf("program on interface changed, not removing\n");
+       }
        exit(0);
 }
 
@@ -53,7 +67,8 @@ static void poll_stats(unsigned int kill_after_s)
                for (proto = 0; proto < nr_protos; proto++) {
                        __u64 sum = 0;
 
-                       assert(bpf_map_lookup_elem(map_fd[0], &proto, values) == 0);
+                       assert(bpf_map_lookup_elem(rxcnt_map_fd, &proto,
+                                                  values) == 0);
                        for (i = 0; i < nr_cpus; i++)
                                sum += (values[i] - prev[proto][i]);
 
@@ -81,6 +96,7 @@ static void usage(const char *cmd)
        printf("    -P <IP-Protocol> Default is TCP\n");
        printf("    -S use skb-mode\n");
        printf("    -N enforce native mode\n");
+       printf("    -F Force loading the XDP prog\n");
        printf("    -h Display this help\n");
 }
 
@@ -138,16 +154,22 @@ static int parse_ports(const char *port_str, int *min_port, int *max_port)
 
 int main(int argc, char **argv)
 {
+       struct bpf_prog_load_attr prog_load_attr = {
+               .prog_type      = BPF_PROG_TYPE_XDP,
+       };
+       struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
+       int min_port = 0, max_port = 0, vip2tnl_map_fd;
+       const char *optstr = "i:a:p:s:d:m:T:P:FSNh";
        unsigned char opt_flags[256] = {};
+       struct bpf_prog_info info = {};
+       __u32 info_len = sizeof(info);
        unsigned int kill_after_s = 0;
-       const char *optstr = "i:a:p:s:d:m:T:P:SNh";
-       int min_port = 0, max_port = 0;
        struct iptnl_info tnl = {};
-       struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
+       struct bpf_object *obj;
        struct vip vip = {};
        char filename[256];
-       int opt;
-       int i;
+       int opt, prog_fd;
+       int i, err;
 
        tnl.family = AF_UNSPEC;
        vip.protocol = IPPROTO_TCP;
@@ -211,6 +233,9 @@ int main(int argc, char **argv)
                case 'N':
                        xdp_flags |= XDP_FLAGS_DRV_MODE;
                        break;
+               case 'F':
+                       xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
+                       break;
                default:
                        usage(argv[0]);
                        return 1;
@@ -232,33 +257,47 @@ int main(int argc, char **argv)
        }
 
        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+       prog_load_attr.file = filename;
 
-       if (load_bpf_file(filename)) {
-               printf("%s", bpf_log_buf);
+       if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
                return 1;
-       }
 
-       if (!prog_fd[0]) {
+       if (!prog_fd) {
                printf("load_bpf_file: %s\n", strerror(errno));
                return 1;
        }
 
+       rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
+       vip2tnl_map_fd = bpf_object__find_map_fd_by_name(obj, "vip2tnl");
+       if (vip2tnl_map_fd < 0 || rxcnt_map_fd < 0) {
+               printf("bpf_object__find_map_fd_by_name failed\n");
+               return 1;
+       }
+
        signal(SIGINT, int_exit);
        signal(SIGTERM, int_exit);
 
        while (min_port <= max_port) {
                vip.dport = htons(min_port++);
-               if (bpf_map_update_elem(map_fd[1], &vip, &tnl, BPF_NOEXIST)) {
+               if (bpf_map_update_elem(vip2tnl_map_fd, &vip, &tnl,
+                                       BPF_NOEXIST)) {
                        perror("bpf_map_update_elem(&vip2tnl)");
                        return 1;
                }
        }
 
-       if (bpf_set_link_xdp_fd(ifindex, prog_fd[0], xdp_flags) < 0) {
+       if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) {
                printf("link set xdp fd failed\n");
                return 1;
        }
 
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (err) {
+               printf("can't get prog info - %s\n", strerror(errno));
+               return err;
+       }
+       prog_id = info.id;
+
        poll_stats(kill_after_s);
 
        bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
index 57ecadc..f73055e 100644 (file)
@@ -68,7 +68,7 @@ enum benchmark_type {
 };
 
 static enum benchmark_type opt_bench = BENCH_RXDROP;
-static u32 opt_xdp_flags;
+static u32 opt_xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
 static const char *opt_if = "";
 static int opt_ifindex;
 static int opt_queue;
@@ -76,6 +76,7 @@ static int opt_poll;
 static int opt_shared_packet_buffer;
 static int opt_interval = 1;
 static u32 opt_xdp_bind_flags;
+static __u32 prog_id;
 
 struct xdp_umem_uqueue {
        u32 cached_prod;
@@ -631,9 +632,20 @@ static void *poller(void *arg)
 
 static void int_exit(int sig)
 {
+       __u32 curr_prog_id = 0;
+
        (void)sig;
        dump_stats();
-       bpf_set_link_xdp_fd(opt_ifindex, -1, opt_xdp_flags);
+       if (bpf_get_link_xdp_id(opt_ifindex, &curr_prog_id, opt_xdp_flags)) {
+               printf("bpf_get_link_xdp_id failed\n");
+               exit(EXIT_FAILURE);
+       }
+       if (prog_id == curr_prog_id)
+               bpf_set_link_xdp_fd(opt_ifindex, -1, opt_xdp_flags);
+       else if (!curr_prog_id)
+               printf("couldn't find a prog id on a given interface\n");
+       else
+               printf("program on interface changed, not removing\n");
        exit(EXIT_SUCCESS);
 }
 
@@ -682,7 +694,7 @@ static void parse_command_line(int argc, char **argv)
        opterr = 0;
 
        for (;;) {
-               c = getopt_long(argc, argv, "rtli:q:psSNn:cz", long_options,
+               c = getopt_long(argc, argv, "Frtli:q:psSNn:cz", long_options,
                                &option_index);
                if (c == -1)
                        break;
@@ -725,6 +737,9 @@ static void parse_command_line(int argc, char **argv)
                case 'c':
                        opt_xdp_bind_flags |= XDP_COPY;
                        break;
+               case 'F':
+                       opt_xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
+                       break;
                default:
                        usage(basename(argv[0]));
                }
@@ -904,6 +919,8 @@ int main(int argc, char **argv)
                .prog_type      = BPF_PROG_TYPE_XDP,
        };
        int prog_fd, qidconf_map, xsks_map;
+       struct bpf_prog_info info = {};
+       __u32 info_len = sizeof(info);
        struct bpf_object *obj;
        char xdp_filename[256];
        struct bpf_map *map;
@@ -950,6 +967,13 @@ int main(int argc, char **argv)
                exit(EXIT_FAILURE);
        }
 
+       ret = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (ret) {
+               printf("can't get prog info - %s\n", strerror(errno));
+               return 1;
+       }
+       prog_id = info.id;
+
        ret = bpf_map_update_elem(qidconf_map, &key, &opt_queue, 0);
        if (ret) {
                fprintf(stderr, "ERROR: bpf_map_update_elem qidconf\n");
index 33e67bd..3223448 100644 (file)
@@ -117,7 +117,7 @@ static bool mei_init(struct mei *me, const uuid_le *guid,
 
        me->verbose = verbose;
 
-       me->fd = open("/dev/mei", O_RDWR);
+       me->fd = open("/dev/mei0", O_RDWR);
        if (me->fd == -1) {
                mei_err(me, "Cannot establish a handle to the Intel MEI driver\n");
                goto err;
index 08c88de..11975ec 100644 (file)
@@ -1444,7 +1444,10 @@ check:
                        new = aa_label_merge(label, target, GFP_KERNEL);
                if (IS_ERR_OR_NULL(new)) {
                        info = "failed to build target label";
-                       error = PTR_ERR(new);
+                       if (!new)
+                               error = -ENOMEM;
+                       else
+                               error = PTR_ERR(new);
                        new = NULL;
                        perms.allow = 0;
                        goto audit;
index 2c01087..8db1731 100644 (file)
@@ -1599,12 +1599,14 @@ static unsigned int apparmor_ipv4_postroute(void *priv,
        return apparmor_ip_postroute(priv, skb, state);
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
 static unsigned int apparmor_ipv6_postroute(void *priv,
                                            struct sk_buff *skb,
                                            const struct nf_hook_state *state)
 {
        return apparmor_ip_postroute(priv, skb, state);
 }
+#endif
 
 static const struct nf_hook_ops apparmor_nf_ops[] = {
        {
index 40013b2..6c0b303 100644 (file)
@@ -2177,16 +2177,11 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
                snd_pcm_update_hw_ptr(substream);
 
        if (!is_playback &&
-           runtime->status->state == SNDRV_PCM_STATE_PREPARED) {
-               if (size >= runtime->start_threshold) {
-                       err = snd_pcm_start(substream);
-                       if (err < 0)
-                               goto _end_unlock;
-               } else {
-                       /* nothing to do */
-                       err = 0;
+           runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
+           size >= runtime->start_threshold) {
+               err = snd_pcm_start(substream);
+               if (err < 0)
                        goto _end_unlock;
-               }
        }
 
        avail = snd_pcm_avail(substream);
index 9174f1b..1ec706c 100644 (file)
@@ -115,7 +115,8 @@ static int hda_codec_driver_probe(struct device *dev)
        err = snd_hda_codec_build_controls(codec);
        if (err < 0)
                goto error_module;
-       if (codec->card->registered) {
+       /* only register after the bus probe finished; otherwise it's racy */
+       if (!codec->bus->bus_probing && codec->card->registered) {
                err = snd_card_register(codec->card);
                if (err < 0)
                        goto error_module;
index e784130..e5c4900 100644 (file)
@@ -2185,6 +2185,7 @@ static int azx_probe_continue(struct azx *chip)
        int dev = chip->dev_index;
        int err;
 
+       to_hda_bus(bus)->bus_probing = 1;
        hda->probe_continued = 1;
 
        /* bind with i915 if needed */
@@ -2269,6 +2270,7 @@ out_free:
        if (err < 0)
                hda->init_failed = 1;
        complete_all(&hda->probe_wait);
+       to_hda_bus(bus)->bus_probing = 0;
        return err;
 }
 
index e5bdbc2..29882bd 100644 (file)
@@ -8451,8 +8451,10 @@ static void ca0132_free(struct hda_codec *codec)
        ca0132_exit_chip(codec);
 
        snd_hda_power_down(codec);
-       if (IS_ENABLED(CONFIG_PCI) && spec->mem_base)
+#ifdef CONFIG_PCI
+       if (spec->mem_base)
                pci_iounmap(codec->bus->pci, spec->mem_base);
+#endif
        kfree(spec->spec_init_verbs);
        kfree(codec->spec);
 }
index 152f541..a4ee765 100644 (file)
@@ -924,6 +924,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
        SND_PCI_QUIRK(0x103c, 0x807C, "HP EliteBook 820 G3", CXT_FIXUP_HP_DOCK),
        SND_PCI_QUIRK(0x103c, 0x80FD, "HP ProBook 640 G2", CXT_FIXUP_HP_DOCK),
        SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK),
+       SND_PCI_QUIRK(0x103c, 0x83b2, "HP EliteBook 840 G5", CXT_FIXUP_HP_DOCK),
        SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK),
        SND_PCI_QUIRK(0x103c, 0x83d3, "HP ProBook 640 G4", CXT_FIXUP_HP_DOCK),
        SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
index b4f4721..6df758a 100644 (file)
@@ -117,6 +117,7 @@ struct alc_spec {
        int codec_variant;      /* flag for other variants */
        unsigned int has_alc5505_dsp:1;
        unsigned int no_depop_delay:1;
+       unsigned int done_hp_init:1;
 
        /* for PLL fix */
        hda_nid_t pll_nid;
@@ -514,6 +515,15 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
        }
 }
 
+/* get a primary headphone pin if available */
+static hda_nid_t alc_get_hp_pin(struct alc_spec *spec)
+{
+       if (spec->gen.autocfg.hp_pins[0])
+               return spec->gen.autocfg.hp_pins[0];
+       if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT)
+               return spec->gen.autocfg.line_out_pins[0];
+       return 0;
+}
 
 /*
  * Realtek SSID verification
@@ -724,9 +734,7 @@ do_sku:
         * 15   : 1 --> enable the function "Mute internal speaker
         *              when the external headphone out jack is plugged"
         */
-       if (!spec->gen.autocfg.hp_pins[0] &&
-           !(spec->gen.autocfg.line_out_pins[0] &&
-             spec->gen.autocfg.line_out_type == AUTO_PIN_HP_OUT)) {
+       if (!alc_get_hp_pin(spec)) {
                hda_nid_t nid;
                tmp = (ass >> 11) & 0x3;        /* HP to chassis */
                nid = ports[tmp];
@@ -2958,7 +2966,7 @@ static void alc282_restore_default_value(struct hda_codec *codec)
 static void alc282_init(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
        bool hp_pin_sense;
        int coef78;
 
@@ -2995,7 +3003,7 @@ static void alc282_init(struct hda_codec *codec)
 static void alc282_shutup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
        bool hp_pin_sense;
        int coef78;
 
@@ -3073,14 +3081,9 @@ static void alc283_restore_default_value(struct hda_codec *codec)
 static void alc283_init(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
        bool hp_pin_sense;
 
-       if (!spec->gen.autocfg.hp_outs) {
-               if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT)
-                       hp_pin = spec->gen.autocfg.line_out_pins[0];
-       }
-
        alc283_restore_default_value(codec);
 
        if (!hp_pin)
@@ -3114,14 +3117,9 @@ static void alc283_init(struct hda_codec *codec)
 static void alc283_shutup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
        bool hp_pin_sense;
 
-       if (!spec->gen.autocfg.hp_outs) {
-               if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT)
-                       hp_pin = spec->gen.autocfg.line_out_pins[0];
-       }
-
        if (!hp_pin) {
                alc269_shutup(codec);
                return;
@@ -3155,7 +3153,7 @@ static void alc283_shutup(struct hda_codec *codec)
 static void alc256_init(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
        bool hp_pin_sense;
 
        if (!hp_pin)
@@ -3191,7 +3189,7 @@ static void alc256_init(struct hda_codec *codec)
 static void alc256_shutup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
        bool hp_pin_sense;
 
        if (!hp_pin) {
@@ -3227,7 +3225,7 @@ static void alc256_shutup(struct hda_codec *codec)
 static void alc225_init(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
        bool hp1_pin_sense, hp2_pin_sense;
 
        if (!hp_pin)
@@ -3270,7 +3268,7 @@ static void alc225_init(struct hda_codec *codec)
 static void alc225_shutup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
        bool hp1_pin_sense, hp2_pin_sense;
 
        if (!hp_pin) {
@@ -3314,7 +3312,7 @@ static void alc225_shutup(struct hda_codec *codec)
 static void alc_default_init(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
        bool hp_pin_sense;
 
        if (!hp_pin)
@@ -3343,7 +3341,7 @@ static void alc_default_init(struct hda_codec *codec)
 static void alc_default_shutup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
        bool hp_pin_sense;
 
        if (!hp_pin) {
@@ -3372,6 +3370,48 @@ static void alc_default_shutup(struct hda_codec *codec)
        snd_hda_shutup_pins(codec);
 }
 
+static void alc294_hp_init(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
+       int i, val;
+
+       if (!hp_pin)
+               return;
+
+       snd_hda_codec_write(codec, hp_pin, 0,
+                           AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+       msleep(100);
+
+       snd_hda_codec_write(codec, hp_pin, 0,
+                           AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+       alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */
+       alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */
+
+       /* Wait for depop procedure finish  */
+       val = alc_read_coefex_idx(codec, 0x58, 0x01);
+       for (i = 0; i < 20 && val & 0x0080; i++) {
+               msleep(50);
+               val = alc_read_coefex_idx(codec, 0x58, 0x01);
+       }
+       /* Set HP depop to auto mode */
+       alc_update_coef_idx(codec, 0x6f, 0x000f, 0x000b);
+       msleep(50);
+}
+
+static void alc294_init(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if (!spec->done_hp_init) {
+               alc294_hp_init(codec);
+               spec->done_hp_init = true;
+       }
+       alc_default_init(codec);
+}
+
 static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
                             unsigned int val)
 {
@@ -4737,7 +4777,7 @@ static void alc_update_headset_mode(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
 
        hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       hda_nid_t hp_pin = alc_get_hp_pin(spec);
 
        int new_headset_mode;
 
@@ -5016,7 +5056,7 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec,
 static void alc_shutup_dell_xps13(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       int hp_pin = spec->gen.autocfg.hp_pins[0];
+       int hp_pin = alc_get_hp_pin(spec);
 
        /* Prevent pop noises when headphones are plugged in */
        snd_hda_codec_write(codec, hp_pin, 0,
@@ -5109,7 +5149,7 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
 
        if (action == HDA_FIXUP_ACT_PROBE) {
                int mic_pin = find_ext_mic_pin(codec);
-               int hp_pin = spec->gen.autocfg.hp_pins[0];
+               int hp_pin = alc_get_hp_pin(spec);
 
                if (snd_BUG_ON(!mic_pin || !hp_pin))
                        return;
@@ -5591,6 +5631,7 @@ enum {
        ALC294_FIXUP_ASUS_HEADSET_MIC,
        ALC294_FIXUP_ASUS_SPK,
        ALC225_FIXUP_HEADSET_JACK,
+       ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -6537,6 +6578,15 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_headset_jack,
        },
+       [ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -6715,6 +6765,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC),
        SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC),
        SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC),
+       SND_PCI_QUIRK(0x1558, 0x1325, "System76 Darter Pro (darp5)", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
        SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
@@ -7373,37 +7424,6 @@ static void alc269_fill_coef(struct hda_codec *codec)
        alc_update_coef_idx(codec, 0x4, 0, 1<<11);
 }
 
-static void alc294_hp_init(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
-       int i, val;
-
-       if (!hp_pin)
-               return;
-
-       snd_hda_codec_write(codec, hp_pin, 0,
-                           AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-
-       msleep(100);
-
-       snd_hda_codec_write(codec, hp_pin, 0,
-                           AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
-
-       alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */
-       alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */
-
-       /* Wait for depop procedure finish  */
-       val = alc_read_coefex_idx(codec, 0x58, 0x01);
-       for (i = 0; i < 20 && val & 0x0080; i++) {
-               msleep(50);
-               val = alc_read_coefex_idx(codec, 0x58, 0x01);
-       }
-       /* Set HP depop to auto mode */
-       alc_update_coef_idx(codec, 0x6f, 0x000f, 0x000b);
-       msleep(50);
-}
-
 /*
  */
 static int patch_alc269(struct hda_codec *codec)
@@ -7529,7 +7549,7 @@ static int patch_alc269(struct hda_codec *codec)
                spec->codec_variant = ALC269_TYPE_ALC294;
                spec->gen.mixer_nid = 0; /* ALC2x4 does not have any loopback mixer path */
                alc_update_coef_idx(codec, 0x6b, 0x0018, (1<<4) | (1<<3)); /* UAJ MIC Vref control by verb */
-               alc294_hp_init(codec);
+               spec->init_hook = alc294_init;
                break;
        case 0x10ec0300:
                spec->codec_variant = ALC269_TYPE_ALC300;
@@ -7541,7 +7561,7 @@ static int patch_alc269(struct hda_codec *codec)
                spec->codec_variant = ALC269_TYPE_ALC700;
                spec->gen.mixer_nid = 0; /* ALC700 does not have any loopback mixer path */
                alc_update_coef_idx(codec, 0x4a, 1 << 15, 0); /* Combo jack auto trigger control */
-               alc294_hp_init(codec);
+               spec->init_hook = alc294_init;
                break;
 
        }
index d00734d..e5b6769 100644 (file)
@@ -795,6 +795,8 @@ static int hdmi_codec_probe(struct platform_device *pdev)
        if (hcd->spdif)
                hcp->daidrv[i] = hdmi_spdif_dai;
 
+       dev_set_drvdata(dev, hcp);
+
        ret = devm_snd_soc_register_component(dev, &hdmi_driver, hcp->daidrv,
                                     dai_count);
        if (ret) {
@@ -802,8 +804,6 @@ static int hdmi_codec_probe(struct platform_device *pdev)
                        __func__, ret);
                return ret;
        }
-
-       dev_set_drvdata(dev, hcp);
        return 0;
 }
 
index 89c43b2..a9b91bc 100644 (file)
@@ -1778,7 +1778,9 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = {
        {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc},
        {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc},
        {"ADC STO1 ASRC", NULL, "AD ASRC"},
+       {"ADC STO1 ASRC", NULL, "DA ASRC"},
        {"ADC STO1 ASRC", NULL, "CLKDET"},
+       {"DAC STO1 ASRC", NULL, "AD ASRC"},
        {"DAC STO1 ASRC", NULL, "DA ASRC"},
        {"DAC STO1 ASRC", NULL, "CLKDET"},
 
index d6c62aa..ce00fe2 100644 (file)
@@ -700,6 +700,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
 {
        struct i2s_dai *i2s = to_info(dai);
        u32 mod, mask = 0, val = 0;
+       struct clk *rclksrc;
        unsigned long flags;
 
        WARN_ON(!pm_runtime_active(dai->dev));
@@ -782,6 +783,10 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
 
        i2s->frmclk = params_rate(params);
 
+       rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC];
+       if (rclksrc && !IS_ERR(rclksrc))
+               i2s->rclk_srcrate = clk_get_rate(rclksrc);
+
        return 0;
 }
 
@@ -886,11 +891,6 @@ static int config_setup(struct i2s_dai *i2s)
                return 0;
 
        if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
-               struct clk *rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC];
-
-               if (rclksrc && !IS_ERR(rclksrc))
-                       i2s->rclk_srcrate = clk_get_rate(rclksrc);
-
                psr = i2s->rclk_srcrate / i2s->frmclk / rfs;
                writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR);
                dev_dbg(&i2s->pdev->dev,
index 59e250c..e819e96 100644 (file)
@@ -1526,14 +1526,14 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
        int ret;
 
        /*
-        * 1) Avoid duplicate register (ex. MIXer case)
-        * 2) re-register if card was rebinded
+        * 1) Avoid duplicate register for DVC with MIX case
+        * 2) Allow duplicate register for MIX
+        * 3) re-register if card was rebinded
         */
        list_for_each_entry(kctrl, &card->controls, list) {
                struct rsnd_kctrl_cfg *c = kctrl->private_data;
 
-               if (strcmp(kctrl->id.name, name) == 0 &&
-                   c->mod == mod)
+               if (c == cfg)
                        return 0;
        }
 
index 45ef295..f5afab6 100644 (file)
@@ -286,7 +286,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
        if (rsnd_ssi_is_multi_slave(mod, io))
                return 0;
 
-       if (ssi->usrcnt > 1) {
+       if (ssi->usrcnt > 0) {
                if (ssi->rate != rate) {
                        dev_err(dev, "SSI parent/child should use same rate\n");
                        return -EINVAL;
index c5934ad..c74991d 100644 (file)
@@ -79,7 +79,7 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
                break;
        case 9:
                for (i = 0; i < 4; i++)
-                       rsnd_mod_write(mod, SSI_SYS_STATUS((i * 2) + 1), 0xf << (id * 4));
+                       rsnd_mod_write(mod, SSI_SYS_STATUS((i * 2) + 1), 0xf << 4);
                break;
        }
 
index aae450b..50617db 100644 (file)
@@ -735,12 +735,17 @@ static struct snd_soc_component *soc_find_component(
        const struct device_node *of_node, const char *name)
 {
        struct snd_soc_component *component;
+       struct device_node *component_of_node;
 
        lockdep_assert_held(&client_mutex);
 
        for_each_component(component) {
                if (of_node) {
-                       if (component->dev->of_node == of_node)
+                       component_of_node = component->dev->of_node;
+                       if (!component_of_node && component->dev->parent)
+                               component_of_node = component->dev->parent->of_node;
+
+                       if (component_of_node == of_node)
                                return component;
                } else if (name && strcmp(component->name, name) == 0) {
                        return component;
@@ -951,7 +956,7 @@ static void soc_remove_dai(struct snd_soc_dai *dai, int order)
 {
        int err;
 
-       if (!dai || !dai->probed ||
+       if (!dai || !dai->probed || !dai->driver ||
            dai->driver->remove_order != order)
                return;
 
index 2c4c134..20bad75 100644 (file)
@@ -70,12 +70,16 @@ static int dapm_up_seq[] = {
        [snd_soc_dapm_clock_supply] = 1,
        [snd_soc_dapm_supply] = 2,
        [snd_soc_dapm_micbias] = 3,
+       [snd_soc_dapm_vmid] = 3,
        [snd_soc_dapm_dai_link] = 2,
        [snd_soc_dapm_dai_in] = 4,
        [snd_soc_dapm_dai_out] = 4,
        [snd_soc_dapm_aif_in] = 4,
        [snd_soc_dapm_aif_out] = 4,
        [snd_soc_dapm_mic] = 5,
+       [snd_soc_dapm_siggen] = 5,
+       [snd_soc_dapm_input] = 5,
+       [snd_soc_dapm_output] = 5,
        [snd_soc_dapm_mux] = 6,
        [snd_soc_dapm_demux] = 6,
        [snd_soc_dapm_dac] = 7,
@@ -83,11 +87,19 @@ static int dapm_up_seq[] = {
        [snd_soc_dapm_mixer] = 8,
        [snd_soc_dapm_mixer_named_ctl] = 8,
        [snd_soc_dapm_pga] = 9,
+       [snd_soc_dapm_buffer] = 9,
+       [snd_soc_dapm_scheduler] = 9,
+       [snd_soc_dapm_effect] = 9,
+       [snd_soc_dapm_src] = 9,
+       [snd_soc_dapm_asrc] = 9,
+       [snd_soc_dapm_encoder] = 9,
+       [snd_soc_dapm_decoder] = 9,
        [snd_soc_dapm_adc] = 10,
        [snd_soc_dapm_out_drv] = 11,
        [snd_soc_dapm_hp] = 11,
        [snd_soc_dapm_spk] = 11,
        [snd_soc_dapm_line] = 11,
+       [snd_soc_dapm_sink] = 11,
        [snd_soc_dapm_kcontrol] = 12,
        [snd_soc_dapm_post] = 13,
 };
@@ -100,13 +112,25 @@ static int dapm_down_seq[] = {
        [snd_soc_dapm_spk] = 3,
        [snd_soc_dapm_line] = 3,
        [snd_soc_dapm_out_drv] = 3,
+       [snd_soc_dapm_sink] = 3,
        [snd_soc_dapm_pga] = 4,
+       [snd_soc_dapm_buffer] = 4,
+       [snd_soc_dapm_scheduler] = 4,
+       [snd_soc_dapm_effect] = 4,
+       [snd_soc_dapm_src] = 4,
+       [snd_soc_dapm_asrc] = 4,
+       [snd_soc_dapm_encoder] = 4,
+       [snd_soc_dapm_decoder] = 4,
        [snd_soc_dapm_switch] = 5,
        [snd_soc_dapm_mixer_named_ctl] = 5,
        [snd_soc_dapm_mixer] = 5,
        [snd_soc_dapm_dac] = 6,
        [snd_soc_dapm_mic] = 7,
+       [snd_soc_dapm_siggen] = 7,
+       [snd_soc_dapm_input] = 7,
+       [snd_soc_dapm_output] = 7,
        [snd_soc_dapm_micbias] = 8,
+       [snd_soc_dapm_vmid] = 8,
        [snd_soc_dapm_mux] = 9,
        [snd_soc_dapm_demux] = 9,
        [snd_soc_dapm_aif_in] = 10,
index 045ef13..fc79ec6 100644 (file)
@@ -502,6 +502,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;
 
        if (pass != SOC_TPLG_PASS_PCM_DAI)
                return;
@@ -509,6 +510,10 @@ static void remove_dai(struct snd_soc_component *comp,
        if (dobj->ops && dobj->ops->dai_unload)
                dobj->ops->dai_unload(comp, dobj);
 
+       list_for_each_entry(dai, &comp->dai_list, list)
+               if (dai->driver == dai_drv)
+                       dai->driver = NULL;
+
        kfree(dai_drv->name);
        list_del(&dobj->list);
        kfree(dai_drv);
index 3828471..db114f3 100644 (file)
@@ -314,6 +314,9 @@ static int search_roland_implicit_fb(struct usb_device *dev, int ifnum,
        return 0;
 }
 
+/* Setup an implicit feedback endpoint from a quirk. Returns 0 if no quirk
+ * applies. Returns 1 if a quirk was found.
+ */
 static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
                                         struct usb_device *dev,
                                         struct usb_interface_descriptor *altsd,
@@ -384,7 +387,7 @@ add_sync_ep:
 
        subs->data_endpoint->sync_master = subs->sync_endpoint;
 
-       return 0;
+       return 1;
 }
 
 static int set_sync_endpoint(struct snd_usb_substream *subs,
@@ -423,6 +426,10 @@ static int set_sync_endpoint(struct snd_usb_substream *subs,
        if (err < 0)
                return err;
 
+       /* endpoint set by quirk */
+       if (err > 0)
+               return 0;
+
        if (altsd->bNumEndpoints < 2)
                return 0;
 
index ebbadb3..7e65fe8 100644 (file)
@@ -1492,6 +1492,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
                        return SNDRV_PCM_FMTBIT_DSD_U32_BE;
                break;
 
+       case USB_ID(0x10cb, 0x0103): /* The Bit Opus #3; with fp->dsd_raw */
        case USB_ID(0x152a, 0x85de): /* SMSL D1 DAC */
        case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */
        case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */
@@ -1566,6 +1567,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
        case 0x20b1:  /* XMOS based devices */
        case 0x152a:  /* Thesycon devices */
        case 0x25ce:  /* Mytek devices */
+       case 0x2ab6:  /* T+A devices */
                if (fp->dsd_raw)
                        return SNDRV_PCM_FMTBIT_DSD_U32_BE;
                break;
index d43fce5..9bb9ace 100644 (file)
@@ -17,8 +17,8 @@ SYNOPSIS
        *COMMANDS* :=
        { **show** | **list** | **tree** | **attach** | **detach** | **help** }
 
-MAP COMMANDS
-=============
+CGROUP COMMANDS
+===============
 
 |      **bpftool** **cgroup { show | list }** *CGROUP*
 |      **bpftool** **cgroup tree** [*CGROUP_ROOT*]
index 8d489a2..82de03d 100644 (file)
@@ -16,8 +16,8 @@ SYNOPSIS
 
        *COMMANDS* := { **probe** | **help** }
 
-MAP COMMANDS
-=============
+FEATURE COMMANDS
+================
 
 |      **bpftool** **feature probe** [*COMPONENT*] [**macros** [**prefix** *PREFIX*]]
 |      **bpftool** **feature help**
index 13b5610..7e59495 100644 (file)
@@ -18,7 +18,7 @@ SYNOPSIS
        { **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load**
        | **loadall** | **help** }
 
-MAP COMMANDS
+PROG COMMANDS
 =============
 
 |      **bpftool** **prog { show | list }** [*PROG*]
index 27153bb..4f21888 100644 (file)
@@ -16,7 +16,7 @@ SYNOPSIS
 
        **bpftool** **version**
 
-       *OBJECT* := { **map** | **program** | **cgroup** | **perf** | **net** }
+       *OBJECT* := { **map** | **program** | **cgroup** | **perf** | **net** | **feature** }
 
        *OPTIONS* := { { **-V** | **--version** } | { **-h** | **--help** }
        | { **-j** | **--json** } [{ **-p** | **--pretty** }] }
@@ -34,6 +34,8 @@ SYNOPSIS
 
        *NET-COMMANDS* := { **show** | **list** | **help** }
 
+       *FEATURE-COMMANDS* := { **probe** | **help** }
+
 DESCRIPTION
 ===========
        *bpftool* allows for inspection and simple modification of BPF objects
index 8974834..f7261fa 100644 (file)
@@ -297,10 +297,8 @@ char *get_fdinfo(int fd, const char *key)
        snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd);
 
        fdi = fopen(path, "r");
-       if (!fdi) {
-               p_err("can't open fdinfo: %s", strerror(errno));
+       if (!fdi)
                return NULL;
-       }
 
        while ((n = getline(&line, &line_n, fdi)) > 0) {
                char *value;
@@ -313,7 +311,6 @@ char *get_fdinfo(int fd, const char *key)
 
                value = strchr(line, '\t');
                if (!value || !value[1]) {
-                       p_err("malformed fdinfo!?");
                        free(line);
                        return NULL;
                }
@@ -326,7 +323,6 @@ char *get_fdinfo(int fd, const char *key)
                return line;
        }
 
-       p_err("key '%s' not found in fdinfo", key);
        free(line);
        fclose(fdi);
        return NULL;
index 2160a8e..e0c650d 100644 (file)
@@ -358,6 +358,20 @@ static char **parse_bytes(char **argv, const char *name, unsigned char *val,
        return argv + i;
 }
 
+/* on per cpu maps we must copy the provided value on all value instances */
+static void fill_per_cpu_value(struct bpf_map_info *info, void *value)
+{
+       unsigned int i, n, step;
+
+       if (!map_is_per_cpu(info->type))
+               return;
+
+       n = get_possible_cpus();
+       step = round_up(info->value_size, 8);
+       for (i = 1; i < n; i++)
+               memcpy(value + i * step, value, info->value_size);
+}
+
 static int parse_elem(char **argv, struct bpf_map_info *info,
                      void *key, void *value, __u32 key_size, __u32 value_size,
                      __u32 *flags, __u32 **value_fd)
@@ -440,6 +454,8 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
                        argv = parse_bytes(argv, "value", value, value_size);
                        if (!argv)
                                return -1;
+
+                       fill_per_cpu_value(info, value);
                }
 
                return parse_elem(argv, info, key, NULL, key_size, value_size,
@@ -511,10 +527,9 @@ static int show_map_close_json(int fd, struct bpf_map_info *info)
                                jsonw_uint_field(json_wtr, "owner_prog_type",
                                                 prog_type);
                }
-               if (atoi(owner_jited))
-                       jsonw_bool_field(json_wtr, "owner_jited", true);
-               else
-                       jsonw_bool_field(json_wtr, "owner_jited", false);
+               if (owner_jited)
+                       jsonw_bool_field(json_wtr, "owner_jited",
+                                        !!atoi(owner_jited));
 
                free(owner_prog_type);
                free(owner_jited);
@@ -567,7 +582,8 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info)
                char *owner_prog_type = get_fdinfo(fd, "owner_prog_type");
                char *owner_jited = get_fdinfo(fd, "owner_jited");
 
-               printf("\n\t");
+               if (owner_prog_type || owner_jited)
+                       printf("\n\t");
                if (owner_prog_type) {
                        unsigned int prog_type = atoi(owner_prog_type);
 
@@ -577,10 +593,9 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info)
                        else
                                printf("owner_prog_type %d  ", prog_type);
                }
-               if (atoi(owner_jited))
-                       printf("owner jited");
-               else
-                       printf("owner not jited");
+               if (owner_jited)
+                       printf("owner%s jited",
+                              atoi(owner_jited) ? "" : " not");
 
                free(owner_prog_type);
                free(owner_jited);
index 0640e9b..33ed080 100644 (file)
@@ -78,13 +78,14 @@ static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
 
 static int prog_fd_by_tag(unsigned char *tag)
 {
-       struct bpf_prog_info info = {};
-       __u32 len = sizeof(info);
        unsigned int id = 0;
        int err;
        int fd;
 
        while (true) {
+               struct bpf_prog_info info = {};
+               __u32 len = sizeof(info);
+
                err = bpf_prog_get_next_id(id, &id);
                if (err) {
                        p_err("%s", strerror(errno));
index 3040830..8454566 100644 (file)
@@ -330,7 +330,7 @@ static const struct option longopts[] = {
 
 int main(int argc, char **argv)
 {
-       unsigned long long num_loops = 2;
+       long long num_loops = 2;
        unsigned long timedelay = 1000000;
        unsigned long buf_len = 128;
 
index fd92ce8..57aaeaf 100644 (file)
@@ -15,6 +15,8 @@
 #include "../../arch/ia64/include/uapi/asm/bitsperlong.h"
 #elif defined(__riscv)
 #include "../../arch/riscv/include/uapi/asm/bitsperlong.h"
+#elif defined(__alpha__)
+#include "../../arch/alpha/include/uapi/asm/bitsperlong.h"
 #else
 #include <asm-generic/bitsperlong.h>
 #endif
index 60b99b7..bcdd247 100644 (file)
@@ -267,6 +267,7 @@ enum bpf_attach_type {
 #define BPF_ANY                0 /* create new element or update existing */
 #define BPF_NOEXIST    1 /* create new element if it didn't exist */
 #define BPF_EXIST      2 /* update existing element */
+#define BPF_F_LOCK     4 /* spin_lock-ed map_lookup/map_update */
 
 /* flags for BPF_MAP_CREATE command */
 #define BPF_F_NO_PREALLOC      (1U << 0)
@@ -2015,6 +2016,19 @@ union bpf_attr {
  *                     Only works if *skb* contains an IPv6 packet. Insert a
  *                     Segment Routing Header (**struct ipv6_sr_hdr**) inside
  *                     the IPv6 header.
+ *             **BPF_LWT_ENCAP_IP**
+ *                     IP encapsulation (GRE/GUE/IPIP/etc). The outer header
+ *                     must be IPv4 or IPv6, followed by zero or more
+ *                     additional headers, up to LWT_BPF_MAX_HEADROOM total
+ *                     bytes in all prepended headers. Please note that
+ *                     if skb_is_gso(skb) is true, no more than two headers
+ *                     can be prepended, and the inner header, if present,
+ *                     should be either GRE or UDP/GUE.
+ *
+ *             BPF_LWT_ENCAP_SEG6*** types can be called by bpf programs of
+ *             type BPF_PROG_TYPE_LWT_IN; BPF_LWT_ENCAP_IP type can be called
+ *             by bpf programs of types BPF_PROG_TYPE_LWT_IN and
+ *             BPF_PROG_TYPE_LWT_XMIT.
  *
  *             A call to this helper is susceptible to change the underlaying
  *             packet buffer. Therefore, at load time, all checks on pointers
@@ -2328,6 +2342,23 @@ union bpf_attr {
  *             "**y**".
  *     Return
  *             0
+ *
+ * struct bpf_sock *bpf_sk_fullsock(struct bpf_sock *sk)
+ *     Description
+ *             This helper gets a **struct bpf_sock** pointer such
+ *             that all the fields in bpf_sock can be accessed.
+ *     Return
+ *             A **struct bpf_sock** pointer on success, or NULL in
+ *             case of failure.
+ *
+ * struct bpf_tcp_sock *bpf_tcp_sock(struct bpf_sock *sk)
+ *     Description
+ *             This helper gets a **struct bpf_tcp_sock** pointer from a
+ *             **struct bpf_sock** pointer.
+ *
+ *     Return
+ *             A **struct bpf_tcp_sock** pointer on success, or NULL in
+ *             case of failure.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -2422,7 +2453,11 @@ union bpf_attr {
        FN(map_peek_elem),              \
        FN(msg_push_data),              \
        FN(msg_pop_data),               \
-       FN(rc_pointer_rel),
+       FN(rc_pointer_rel),             \
+       FN(spin_lock),                  \
+       FN(spin_unlock),                \
+       FN(sk_fullsock),                \
+       FN(tcp_sock),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -2495,7 +2530,8 @@ enum bpf_hdr_start_off {
 /* Encapsulation type for BPF_FUNC_lwt_push_encap helper. */
 enum bpf_lwt_encap_mode {
        BPF_LWT_ENCAP_SEG6,
-       BPF_LWT_ENCAP_SEG6_INLINE
+       BPF_LWT_ENCAP_SEG6_INLINE,
+       BPF_LWT_ENCAP_IP,
 };
 
 #define __bpf_md_ptr(type, name)       \
@@ -2542,6 +2578,7 @@ struct __sk_buff {
        __u64 tstamp;
        __u32 wire_len;
        __u32 gso_segs;
+       __bpf_md_ptr(struct bpf_sock *, sk);
 };
 
 struct bpf_tunnel_key {
@@ -2583,7 +2620,15 @@ enum bpf_ret_code {
        BPF_DROP = 2,
        /* 3-6 reserved */
        BPF_REDIRECT = 7,
-       /* >127 are reserved for prog type specific return codes */
+       /* >127 are reserved for prog type specific return codes.
+        *
+        * BPF_LWT_REROUTE: used by BPF_PROG_TYPE_LWT_IN and
+        *    BPF_PROG_TYPE_LWT_XMIT to indicate that skb had been
+        *    changed and should be routed based on its new L3 header.
+        *    (This is an L3 redirect, as opposed to L2 redirect
+        *    represented by BPF_REDIRECT above).
+        */
+       BPF_LWT_REROUTE = 128,
 };
 
 struct bpf_sock {
@@ -2593,14 +2638,52 @@ struct bpf_sock {
        __u32 protocol;
        __u32 mark;
        __u32 priority;
-       __u32 src_ip4;          /* Allows 1,2,4-byte read.
-                                * Stored in network byte order.
+       /* IP address also allows 1 and 2 bytes access */
+       __u32 src_ip4;
+       __u32 src_ip6[4];
+       __u32 src_port;         /* host byte order */
+       __u32 dst_port;         /* network byte order */
+       __u32 dst_ip4;
+       __u32 dst_ip6[4];
+       __u32 state;
+};
+
+struct bpf_tcp_sock {
+       __u32 snd_cwnd;         /* Sending congestion window            */
+       __u32 srtt_us;          /* smoothed round trip time << 3 in usecs */
+       __u32 rtt_min;
+       __u32 snd_ssthresh;     /* Slow start size threshold            */
+       __u32 rcv_nxt;          /* What we want to receive next         */
+       __u32 snd_nxt;          /* Next sequence we send                */
+       __u32 snd_una;          /* First byte we want an ack for        */
+       __u32 mss_cache;        /* Cached effective mss, not including SACKS */
+       __u32 ecn_flags;        /* ECN status bits.                     */
+       __u32 rate_delivered;   /* saved rate sample: packets delivered */
+       __u32 rate_interval_us; /* saved rate sample: time elapsed */
+       __u32 packets_out;      /* Packets which are "in flight"        */
+       __u32 retrans_out;      /* Retransmitted packets out            */
+       __u32 total_retrans;    /* Total retransmits for entire connection */
+       __u32 segs_in;          /* RFC4898 tcpEStatsPerfSegsIn
+                                * total number of segments in.
                                 */
-       __u32 src_ip6[4];       /* Allows 1,2,4-byte read.
-                                * Stored in network byte order.
+       __u32 data_segs_in;     /* RFC4898 tcpEStatsPerfDataSegsIn
+                                * total number of data segments in.
                                 */
-       __u32 src_port;         /* Allows 4-byte read.
-                                * Stored in host byte order
+       __u32 segs_out;         /* RFC4898 tcpEStatsPerfSegsOut
+                                * The total number of segments sent.
+                                */
+       __u32 data_segs_out;    /* RFC4898 tcpEStatsPerfDataSegsOut
+                                * total number of data segments sent.
+                                */
+       __u32 lost_out;         /* Lost packets                 */
+       __u32 sacked_out;       /* SACK'd packets                       */
+       __u64 bytes_received;   /* RFC4898 tcpEStatsAppHCThruOctetsReceived
+                                * sum(delta(rcv_nxt)), or how many bytes
+                                * were acked.
+                                */
+       __u64 bytes_acked;      /* RFC4898 tcpEStatsAppHCThruOctetsAcked
+                                * sum(delta(snd_una)), or how many bytes
+                                * were acked.
                                 */
 };
 
@@ -3056,4 +3139,7 @@ struct bpf_line_info {
        __u32   line_col;
 };
 
+struct bpf_spin_lock {
+       __u32   val;
+};
 #endif /* _UAPI__LINUX_BPF_H__ */
index d653382..5b225ff 100644 (file)
@@ -925,6 +925,7 @@ enum {
 enum {
        LINK_XSTATS_TYPE_UNSPEC,
        LINK_XSTATS_TYPE_BRIDGE,
+       LINK_XSTATS_TYPE_BOND,
        __LINK_XSTATS_TYPE_MAX
 };
 #define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1)
index f6052e7..a55cb8b 100644 (file)
@@ -268,7 +268,7 @@ struct sockaddr_in {
 #define        IN_MULTICAST(a)         IN_CLASSD(a)
 #define        IN_MULTICAST_NET        0xe0000000
 
-#define        IN_BADCLASS(a)          ((((long int) (a) ) == 0xffffffff)
+#define        IN_BADCLASS(a)          (((long int) (a) ) == (long int)0xffffffff)
 #define        IN_EXPERIMENTAL(a)      IN_BADCLASS((a))
 
 #define        IN_CLASSE(a)            ((((long int) (a)) & 0xf0000000) == 0xf0000000)
index 6e89a5d..653c4f9 100644 (file)
@@ -13,8 +13,6 @@
 
 #include <linux/pkt_cls.h>
 
-#define TCA_ACT_BPF 13
-
 struct tc_act_bpf {
        tc_gen;
 };
index 88cbd11..9cd0155 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 #include <stdlib.h>
+#include <string.h>
 #include <memory.h>
 #include <unistd.h>
 #include <asm/unistd.h>
@@ -214,23 +215,35 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
 {
        void *finfo = NULL, *linfo = NULL;
        union bpf_attr attr;
+       __u32 log_level;
        __u32 name_len;
        int fd;
 
-       if (!load_attr)
+       if (!load_attr || !log_buf != !log_buf_sz)
+               return -EINVAL;
+
+       log_level = load_attr->log_level;
+       if (log_level > 2 || (log_level && !log_buf))
                return -EINVAL;
 
        name_len = load_attr->name ? strlen(load_attr->name) : 0;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.prog_type = load_attr->prog_type;
        attr.expected_attach_type = load_attr->expected_attach_type;
        attr.insn_cnt = (__u32)load_attr->insns_cnt;
        attr.insns = ptr_to_u64(load_attr->insns);
        attr.license = ptr_to_u64(load_attr->license);
-       attr.log_buf = ptr_to_u64(NULL);
-       attr.log_size = 0;
-       attr.log_level = 0;
+
+       attr.log_level = log_level;
+       if (log_level) {
+               attr.log_buf = ptr_to_u64(log_buf);
+               attr.log_size = log_buf_sz;
+       } else {
+               attr.log_buf = ptr_to_u64(NULL);
+               attr.log_size = 0;
+       }
+
        attr.kern_version = load_attr->kern_version;
        attr.prog_ifindex = load_attr->prog_ifindex;
        attr.prog_btf_fd = load_attr->prog_btf_fd;
@@ -286,7 +299,7 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
                        goto done;
        }
 
-       if (!log_buf || !log_buf_sz)
+       if (log_level || !log_buf)
                goto done;
 
        /* Try again with log */
@@ -327,7 +340,7 @@ int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.prog_type = type;
        attr.insn_cnt = (__u32)insns_cnt;
        attr.insns = ptr_to_u64(insns);
@@ -347,7 +360,7 @@ int bpf_map_update_elem(int fd, const void *key, const void *value,
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.map_fd = fd;
        attr.key = ptr_to_u64(key);
        attr.value = ptr_to_u64(value);
@@ -360,7 +373,7 @@ int bpf_map_lookup_elem(int fd, const void *key, void *value)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.map_fd = fd;
        attr.key = ptr_to_u64(key);
        attr.value = ptr_to_u64(value);
@@ -368,11 +381,24 @@ int bpf_map_lookup_elem(int fd, const void *key, void *value)
        return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
 }
 
+int bpf_map_lookup_elem_flags(int fd, const void *key, void *value, __u64 flags)
+{
+       union bpf_attr attr;
+
+       memset(&attr, 0, sizeof(attr));
+       attr.map_fd = fd;
+       attr.key = ptr_to_u64(key);
+       attr.value = ptr_to_u64(value);
+       attr.flags = flags;
+
+       return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
+}
+
 int bpf_map_lookup_and_delete_elem(int fd, const void *key, void *value)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.map_fd = fd;
        attr.key = ptr_to_u64(key);
        attr.value = ptr_to_u64(value);
@@ -384,7 +410,7 @@ int bpf_map_delete_elem(int fd, const void *key)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.map_fd = fd;
        attr.key = ptr_to_u64(key);
 
@@ -395,7 +421,7 @@ int bpf_map_get_next_key(int fd, const void *key, void *next_key)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.map_fd = fd;
        attr.key = ptr_to_u64(key);
        attr.next_key = ptr_to_u64(next_key);
@@ -407,7 +433,7 @@ int bpf_obj_pin(int fd, const char *pathname)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.pathname = ptr_to_u64((void *)pathname);
        attr.bpf_fd = fd;
 
@@ -418,7 +444,7 @@ int bpf_obj_get(const char *pathname)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.pathname = ptr_to_u64((void *)pathname);
 
        return sys_bpf(BPF_OBJ_GET, &attr, sizeof(attr));
@@ -429,7 +455,7 @@ int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type,
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.target_fd     = target_fd;
        attr.attach_bpf_fd = prog_fd;
        attr.attach_type   = type;
@@ -442,7 +468,7 @@ int bpf_prog_detach(int target_fd, enum bpf_attach_type type)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.target_fd   = target_fd;
        attr.attach_type = type;
 
@@ -453,7 +479,7 @@ int bpf_prog_detach2(int prog_fd, int target_fd, enum bpf_attach_type type)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.target_fd   = target_fd;
        attr.attach_bpf_fd = prog_fd;
        attr.attach_type = type;
@@ -467,7 +493,7 @@ int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
        union bpf_attr attr;
        int ret;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.query.target_fd    = target_fd;
        attr.query.attach_type  = type;
        attr.query.query_flags  = query_flags;
@@ -488,7 +514,7 @@ int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
        union bpf_attr attr;
        int ret;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.test.prog_fd = prog_fd;
        attr.test.data_in = ptr_to_u64(data);
        attr.test.data_out = ptr_to_u64(data_out);
@@ -513,7 +539,7 @@ int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr)
        if (!test_attr->data_out && test_attr->data_size_out > 0)
                return -EINVAL;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.test.prog_fd = test_attr->prog_fd;
        attr.test.data_in = ptr_to_u64(test_attr->data_in);
        attr.test.data_out = ptr_to_u64(test_attr->data_out);
@@ -533,7 +559,7 @@ int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
        union bpf_attr attr;
        int err;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.start_id = start_id;
 
        err = sys_bpf(BPF_PROG_GET_NEXT_ID, &attr, sizeof(attr));
@@ -548,7 +574,7 @@ int bpf_map_get_next_id(__u32 start_id, __u32 *next_id)
        union bpf_attr attr;
        int err;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.start_id = start_id;
 
        err = sys_bpf(BPF_MAP_GET_NEXT_ID, &attr, sizeof(attr));
@@ -562,7 +588,7 @@ int bpf_prog_get_fd_by_id(__u32 id)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.prog_id = id;
 
        return sys_bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
@@ -572,7 +598,7 @@ int bpf_map_get_fd_by_id(__u32 id)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.map_id = id;
 
        return sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
@@ -582,7 +608,7 @@ int bpf_btf_get_fd_by_id(__u32 id)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.btf_id = id;
 
        return sys_bpf(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr));
@@ -593,7 +619,7 @@ int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len)
        union bpf_attr attr;
        int err;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.info.bpf_fd = prog_fd;
        attr.info.info_len = *info_len;
        attr.info.info = ptr_to_u64(info);
@@ -609,7 +635,7 @@ int bpf_raw_tracepoint_open(const char *name, int prog_fd)
 {
        union bpf_attr attr;
 
-       bzero(&attr, sizeof(attr));
+       memset(&attr, 0, sizeof(attr));
        attr.raw_tracepoint.name = ptr_to_u64(name);
        attr.raw_tracepoint.prog_fd = prog_fd;
 
index 8f09de4..6ffdd79 100644 (file)
@@ -85,6 +85,7 @@ struct bpf_load_program_attr {
        __u32 line_info_rec_size;
        const void *line_info;
        __u32 line_info_cnt;
+       __u32 log_level;
 };
 
 /* Flags to direct loading requirements */
@@ -110,6 +111,8 @@ LIBBPF_API int bpf_map_update_elem(int fd, const void *key, const void *value,
                                   __u64 flags);
 
 LIBBPF_API int bpf_map_lookup_elem(int fd, const void *key, void *value);
+LIBBPF_API int bpf_map_lookup_elem_flags(int fd, const void *key, void *value,
+                                        __u64 flags);
 LIBBPF_API int bpf_map_lookup_and_delete_elem(int fd, const void *key,
                                              void *value);
 LIBBPF_API int bpf_map_delete_elem(int fd, const void *key);
index d682d3b..68b50e9 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
 /* Copyright (c) 2018 Facebook */
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <linux/btf.h>
 #include "btf.h"
 #include "bpf.h"
+#include "libbpf.h"
+#include "libbpf_util.h"
 
-#define elog(fmt, ...) { if (err_log) err_log(fmt, ##__VA_ARGS__); }
 #define max(a, b) ((a) > (b) ? (a) : (b))
 #define min(a, b) ((a) < (b) ? (a) : (b))
 
-#define BTF_MAX_NR_TYPES 65535
+#define BTF_MAX_NR_TYPES 0x7fffffff
+#define BTF_MAX_STR_OFFSET 0x7fffffff
 
 #define IS_MODIFIER(k) (((k) == BTF_KIND_TYPEDEF) || \
                ((k) == BTF_KIND_VOLATILE) || \
@@ -39,9 +42,8 @@ struct btf {
 
 struct btf_ext_info {
        /*
-        * info points to a deep copy of the individual info section
-        * (e.g. func_info and line_info) from the .BTF.ext.
-        * It does not include the __u32 rec_size.
+        * info points to the individual info section (e.g. func_info and
+        * line_info) from the .BTF.ext. It does not include the __u32 rec_size.
         */
        void *info;
        __u32 rec_size;
@@ -49,8 +51,13 @@ struct btf_ext_info {
 };
 
 struct btf_ext {
+       union {
+               struct btf_ext_header *hdr;
+               void *data;
+       };
        struct btf_ext_info func_info;
        struct btf_ext_info line_info;
+       __u32 data_size;
 };
 
 struct btf_ext_info_sec {
@@ -107,54 +114,54 @@ static int btf_add_type(struct btf *btf, struct btf_type *t)
        return 0;
 }
 
-static int btf_parse_hdr(struct btf *btf, btf_print_fn_t err_log)
+static int btf_parse_hdr(struct btf *btf)
 {
        const struct btf_header *hdr = btf->hdr;
        __u32 meta_left;
 
        if (btf->data_size < sizeof(struct btf_header)) {
-               elog("BTF header not found\n");
+               pr_debug("BTF header not found\n");
                return -EINVAL;
        }
 
        if (hdr->magic != BTF_MAGIC) {
-               elog("Invalid BTF magic:%x\n", hdr->magic);
+               pr_debug("Invalid BTF magic:%x\n", hdr->magic);
                return -EINVAL;
        }
 
        if (hdr->version != BTF_VERSION) {
-               elog("Unsupported BTF version:%u\n", hdr->version);
+               pr_debug("Unsupported BTF version:%u\n", hdr->version);
                return -ENOTSUP;
        }
 
        if (hdr->flags) {
-               elog("Unsupported BTF flags:%x\n", hdr->flags);
+               pr_debug("Unsupported BTF flags:%x\n", hdr->flags);
                return -ENOTSUP;
        }
 
        meta_left = btf->data_size - sizeof(*hdr);
        if (!meta_left) {
-               elog("BTF has no data\n");
+               pr_debug("BTF has no data\n");
                return -EINVAL;
        }
 
        if (meta_left < hdr->type_off) {
-               elog("Invalid BTF type section offset:%u\n", hdr->type_off);
+               pr_debug("Invalid BTF type section offset:%u\n", hdr->type_off);
                return -EINVAL;
        }
 
        if (meta_left < hdr->str_off) {
-               elog("Invalid BTF string section offset:%u\n", hdr->str_off);
+               pr_debug("Invalid BTF string section offset:%u\n", hdr->str_off);
                return -EINVAL;
        }
 
        if (hdr->type_off >= hdr->str_off) {
-               elog("BTF type section offset >= string section offset. No type?\n");
+               pr_debug("BTF type section offset >= string section offset. No type?\n");
                return -EINVAL;
        }
 
        if (hdr->type_off & 0x02) {
-               elog("BTF type section is not aligned to 4 bytes\n");
+               pr_debug("BTF type section is not aligned to 4 bytes\n");
                return -EINVAL;
        }
 
@@ -163,15 +170,15 @@ static int btf_parse_hdr(struct btf *btf, btf_print_fn_t err_log)
        return 0;
 }
 
-static int btf_parse_str_sec(struct btf *btf, btf_print_fn_t err_log)
+static int btf_parse_str_sec(struct btf *btf)
 {
        const struct btf_header *hdr = btf->hdr;
        const char *start = btf->nohdr_data + hdr->str_off;
        const char *end = start + btf->hdr->str_len;
 
-       if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_NAME_OFFSET ||
+       if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_STR_OFFSET ||
            start[0] || end[-1]) {
-               elog("Invalid BTF string section\n");
+               pr_debug("Invalid BTF string section\n");
                return -EINVAL;
        }
 
@@ -180,7 +187,38 @@ static int btf_parse_str_sec(struct btf *btf, btf_print_fn_t err_log)
        return 0;
 }
 
-static int btf_parse_type_sec(struct btf *btf, btf_print_fn_t err_log)
+static int btf_type_size(struct btf_type *t)
+{
+       int base_size = sizeof(struct btf_type);
+       __u16 vlen = BTF_INFO_VLEN(t->info);
+
+       switch (BTF_INFO_KIND(t->info)) {
+       case BTF_KIND_FWD:
+       case BTF_KIND_CONST:
+       case BTF_KIND_VOLATILE:
+       case BTF_KIND_RESTRICT:
+       case BTF_KIND_PTR:
+       case BTF_KIND_TYPEDEF:
+       case BTF_KIND_FUNC:
+               return base_size;
+       case BTF_KIND_INT:
+               return base_size + sizeof(__u32);
+       case BTF_KIND_ENUM:
+               return base_size + vlen * sizeof(struct btf_enum);
+       case BTF_KIND_ARRAY:
+               return base_size + sizeof(struct btf_array);
+       case BTF_KIND_STRUCT:
+       case BTF_KIND_UNION:
+               return base_size + vlen * sizeof(struct btf_member);
+       case BTF_KIND_FUNC_PROTO:
+               return base_size + vlen * sizeof(struct btf_param);
+       default:
+               pr_debug("Unsupported BTF_KIND:%u\n", BTF_INFO_KIND(t->info));
+               return -EINVAL;
+       }
+}
+
+static int btf_parse_type_sec(struct btf *btf)
 {
        struct btf_header *hdr = btf->hdr;
        void *nohdr_data = btf->nohdr_data;
@@ -189,41 +227,13 @@ static int btf_parse_type_sec(struct btf *btf, btf_print_fn_t err_log)
 
        while (next_type < end_type) {
                struct btf_type *t = next_type;
-               __u16 vlen = BTF_INFO_VLEN(t->info);
+               int type_size;
                int err;
 
-               next_type += sizeof(*t);
-               switch (BTF_INFO_KIND(t->info)) {
-               case BTF_KIND_INT:
-                       next_type += sizeof(int);
-                       break;
-               case BTF_KIND_ARRAY:
-                       next_type += sizeof(struct btf_array);
-                       break;
-               case BTF_KIND_STRUCT:
-               case BTF_KIND_UNION:
-                       next_type += vlen * sizeof(struct btf_member);
-                       break;
-               case BTF_KIND_ENUM:
-                       next_type += vlen * sizeof(struct btf_enum);
-                       break;
-               case BTF_KIND_FUNC_PROTO:
-                       next_type += vlen * sizeof(struct btf_param);
-                       break;
-               case BTF_KIND_FUNC:
-               case BTF_KIND_TYPEDEF:
-               case BTF_KIND_PTR:
-               case BTF_KIND_FWD:
-               case BTF_KIND_VOLATILE:
-               case BTF_KIND_CONST:
-               case BTF_KIND_RESTRICT:
-                       break;
-               default:
-                       elog("Unsupported BTF_KIND:%u\n",
-                            BTF_INFO_KIND(t->info));
-                       return -EINVAL;
-               }
-
+               type_size = btf_type_size(t);
+               if (type_size < 0)
+                       return type_size;
+               next_type += type_size;
                err = btf_add_type(btf, t);
                if (err)
                        return err;
@@ -232,6 +242,11 @@ static int btf_parse_type_sec(struct btf *btf, btf_print_fn_t err_log)
        return 0;
 }
 
+__u32 btf__get_nr_types(const struct btf *btf)
+{
+       return btf->nr_types;
+}
+
 const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 type_id)
 {
        if (type_id > btf->nr_types)
@@ -250,21 +265,6 @@ static bool btf_type_is_void_or_null(const struct btf_type *t)
        return !t || btf_type_is_void(t);
 }
 
-static __s64 btf_type_size(const struct btf_type *t)
-{
-       switch (BTF_INFO_KIND(t->info)) {
-       case BTF_KIND_INT:
-       case BTF_KIND_STRUCT:
-       case BTF_KIND_UNION:
-       case BTF_KIND_ENUM:
-               return t->size;
-       case BTF_KIND_PTR:
-               return sizeof(void *);
-       default:
-               return -EINVAL;
-       }
-}
-
 #define MAX_RESOLVE_DEPTH 32
 
 __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
@@ -278,11 +278,16 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
        t = btf__type_by_id(btf, type_id);
        for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t);
             i++) {
-               size = btf_type_size(t);
-               if (size >= 0)
-                       break;
-
                switch (BTF_INFO_KIND(t->info)) {
+               case BTF_KIND_INT:
+               case BTF_KIND_STRUCT:
+               case BTF_KIND_UNION:
+               case BTF_KIND_ENUM:
+                       size = t->size;
+                       goto done;
+               case BTF_KIND_PTR:
+                       size = sizeof(void *);
+                       goto done;
                case BTF_KIND_TYPEDEF:
                case BTF_KIND_VOLATILE:
                case BTF_KIND_CONST:
@@ -306,6 +311,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
        if (size < 0)
                return -EINVAL;
 
+done:
        if (nelems && size > UINT32_MAX / nelems)
                return -E2BIG;
 
@@ -363,10 +369,8 @@ void btf__free(struct btf *btf)
        free(btf);
 }
 
-struct btf *btf__new(__u8 *data, __u32 size, btf_print_fn_t err_log)
+struct btf *btf__new(__u8 *data, __u32 size)
 {
-       __u32 log_buf_size = 0;
-       char *log_buf = NULL;
        struct btf *btf;
        int err;
 
@@ -376,16 +380,6 @@ struct btf *btf__new(__u8 *data, __u32 size, btf_print_fn_t err_log)
 
        btf->fd = -1;
 
-       if (err_log) {
-               log_buf = malloc(BPF_LOG_BUF_SIZE);
-               if (!log_buf) {
-                       err = -ENOMEM;
-                       goto done;
-               }
-               *log_buf = 0;
-               log_buf_size = BPF_LOG_BUF_SIZE;
-       }
-
        btf->data = malloc(size);
        if (!btf->data) {
                err = -ENOMEM;
@@ -395,30 +389,17 @@ struct btf *btf__new(__u8 *data, __u32 size, btf_print_fn_t err_log)
        memcpy(btf->data, data, size);
        btf->data_size = size;
 
-       btf->fd = bpf_load_btf(btf->data, btf->data_size,
-                              log_buf, log_buf_size, false);
-
-       if (btf->fd == -1) {
-               err = -errno;
-               elog("Error loading BTF: %s(%d)\n", strerror(errno), errno);
-               if (log_buf && *log_buf)
-                       elog("%s\n", log_buf);
-               goto done;
-       }
-
-       err = btf_parse_hdr(btf, err_log);
+       err = btf_parse_hdr(btf);
        if (err)
                goto done;
 
-       err = btf_parse_str_sec(btf, err_log);
+       err = btf_parse_str_sec(btf);
        if (err)
                goto done;
 
-       err = btf_parse_type_sec(btf, err_log);
+       err = btf_parse_type_sec(btf);
 
 done:
-       free(log_buf);
-
        if (err) {
                btf__free(btf);
                return ERR_PTR(err);
@@ -427,11 +408,47 @@ done:
        return btf;
 }
 
+int btf__load(struct btf *btf)
+{
+       __u32 log_buf_size = BPF_LOG_BUF_SIZE;
+       char *log_buf = NULL;
+       int err = 0;
+
+       if (btf->fd >= 0)
+               return -EEXIST;
+
+       log_buf = malloc(log_buf_size);
+       if (!log_buf)
+               return -ENOMEM;
+
+       *log_buf = 0;
+
+       btf->fd = bpf_load_btf(btf->data, btf->data_size,
+                              log_buf, log_buf_size, false);
+       if (btf->fd < 0) {
+               err = -errno;
+               pr_warning("Error loading BTF: %s(%d)\n", strerror(errno), errno);
+               if (*log_buf)
+                       pr_warning("%s\n", log_buf);
+               goto done;
+       }
+
+done:
+       free(log_buf);
+       return err;
+}
+
 int btf__fd(const struct btf *btf)
 {
        return btf->fd;
 }
 
+const void *btf__get_raw_data(const struct btf *btf, __u32 *size)
+{
+       *size = btf->data_size;
+       return btf->data;
+}
+
 const char *btf__name_by_offset(const struct btf *btf, __u32 offset)
 {
        if (offset < btf->hdr->str_len)
@@ -467,7 +484,7 @@ int btf__get_from_id(__u32 id, struct btf **btf)
                goto exit_free;
        }
 
-       bzero(ptr, last_size);
+       memset(ptr, 0, last_size);
        btf_info.btf = ptr_to_u64(ptr);
        err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
 
@@ -481,7 +498,7 @@ int btf__get_from_id(__u32 id, struct btf **btf)
                        goto exit_free;
                }
                ptr = temp_ptr;
-               bzero(ptr, last_size);
+               memset(ptr, 0, last_size);
                btf_info.btf = ptr_to_u64(ptr);
                err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
        }
@@ -491,7 +508,7 @@ int btf__get_from_id(__u32 id, struct btf **btf)
                goto exit_free;
        }
 
-       *btf = btf__new((__u8 *)(long)btf_info.btf, btf_info.btf_size, NULL);
+       *btf = btf__new((__u8 *)(long)btf_info.btf, btf_info.btf_size);
        if (IS_ERR(*btf)) {
                err = PTR_ERR(*btf);
                *btf = NULL;
@@ -504,7 +521,79 @@ exit_free:
        return err;
 }
 
-struct btf_ext_sec_copy_param {
+int btf__get_map_kv_tids(const struct btf *btf, const char *map_name,
+                        __u32 expected_key_size, __u32 expected_value_size,
+                        __u32 *key_type_id, __u32 *value_type_id)
+{
+       const struct btf_type *container_type;
+       const struct btf_member *key, *value;
+       const size_t max_name = 256;
+       char container_name[max_name];
+       __s64 key_size, value_size;
+       __s32 container_id;
+
+       if (snprintf(container_name, max_name, "____btf_map_%s", map_name) ==
+           max_name) {
+               pr_warning("map:%s length of '____btf_map_%s' is too long\n",
+                          map_name, map_name);
+               return -EINVAL;
+       }
+
+       container_id = btf__find_by_name(btf, container_name);
+       if (container_id < 0) {
+               pr_debug("map:%s container_name:%s cannot be found in BTF. Missing BPF_ANNOTATE_KV_PAIR?\n",
+                        map_name, container_name);
+               return container_id;
+       }
+
+       container_type = btf__type_by_id(btf, container_id);
+       if (!container_type) {
+               pr_warning("map:%s cannot find BTF type for container_id:%u\n",
+                          map_name, container_id);
+               return -EINVAL;
+       }
+
+       if (BTF_INFO_KIND(container_type->info) != BTF_KIND_STRUCT ||
+           BTF_INFO_VLEN(container_type->info) < 2) {
+               pr_warning("map:%s container_name:%s is an invalid container struct\n",
+                          map_name, container_name);
+               return -EINVAL;
+       }
+
+       key = (struct btf_member *)(container_type + 1);
+       value = key + 1;
+
+       key_size = btf__resolve_size(btf, key->type);
+       if (key_size < 0) {
+               pr_warning("map:%s invalid BTF key_type_size\n", map_name);
+               return key_size;
+       }
+
+       if (expected_key_size != key_size) {
+               pr_warning("map:%s btf_key_type_size:%u != map_def_key_size:%u\n",
+                          map_name, (__u32)key_size, expected_key_size);
+               return -EINVAL;
+       }
+
+       value_size = btf__resolve_size(btf, value->type);
+       if (value_size < 0) {
+               pr_warning("map:%s invalid BTF value_type_size\n", map_name);
+               return value_size;
+       }
+
+       if (expected_value_size != value_size) {
+               pr_warning("map:%s btf_value_type_size:%u != map_def_value_size:%u\n",
+                          map_name, (__u32)value_size, expected_value_size);
+               return -EINVAL;
+       }
+
+       *key_type_id = key->type;
+       *value_type_id = value->type;
+
+       return 0;
+}
+
+struct btf_ext_sec_setup_param {
        __u32 off;
        __u32 len;
        __u32 min_rec_size;
@@ -512,41 +601,33 @@ struct btf_ext_sec_copy_param {
        const char *desc;
 };
 
-static int btf_ext_copy_info(struct btf_ext *btf_ext,
-                            __u8 *data, __u32 data_size,
-                            struct btf_ext_sec_copy_param *ext_sec,
-                            btf_print_fn_t err_log)
+static int btf_ext_setup_info(struct btf_ext *btf_ext,
+                             struct btf_ext_sec_setup_param *ext_sec)
 {
-       const struct btf_ext_header *hdr = (struct btf_ext_header *)data;
        const struct btf_ext_info_sec *sinfo;
        struct btf_ext_info *ext_info;
        __u32 info_left, record_size;
        /* The start of the info sec (including the __u32 record_size). */
-       const void *info;
-
-       /* data and data_size do not include btf_ext_header from now on */
-       data = data + hdr->hdr_len;
-       data_size -= hdr->hdr_len;
+       void *info;
 
        if (ext_sec->off & 0x03) {
-               elog(".BTF.ext %s section is not aligned to 4 bytes\n",
+               pr_debug(".BTF.ext %s section is not aligned to 4 bytes\n",
                     ext_sec->desc);
                return -EINVAL;
        }
 
-       if (data_size < ext_sec->off ||
-           ext_sec->len > data_size - ext_sec->off) {
-               elog("%s section (off:%u len:%u) is beyond the end of the ELF section .BTF.ext\n",
-                    ext_sec->desc, ext_sec->off, ext_sec->len);
+       info = btf_ext->data + btf_ext->hdr->hdr_len + ext_sec->off;
+       info_left = ext_sec->len;
+
+       if (btf_ext->data + btf_ext->data_size < info + ext_sec->len) {
+               pr_debug("%s section (off:%u len:%u) is beyond the end of the ELF section .BTF.ext\n",
+                        ext_sec->desc, ext_sec->off, ext_sec->len);
                return -EINVAL;
        }
 
-       info = data + ext_sec->off;
-       info_left = ext_sec->len;
-
        /* At least a record size */
        if (info_left < sizeof(__u32)) {
-               elog(".BTF.ext %s record size not found\n", ext_sec->desc);
+               pr_debug(".BTF.ext %s record size not found\n", ext_sec->desc);
                return -EINVAL;
        }
 
@@ -554,8 +635,8 @@ static int btf_ext_copy_info(struct btf_ext *btf_ext,
        record_size = *(__u32 *)info;
        if (record_size < ext_sec->min_rec_size ||
            record_size & 0x03) {
-               elog("%s section in .BTF.ext has invalid record size %u\n",
-                    ext_sec->desc, record_size);
+               pr_debug("%s section in .BTF.ext has invalid record size %u\n",
+                        ext_sec->desc, record_size);
                return -EINVAL;
        }
 
@@ -564,7 +645,7 @@ static int btf_ext_copy_info(struct btf_ext *btf_ext,
 
        /* If no records, return failure now so .BTF.ext won't be used. */
        if (!info_left) {
-               elog("%s section in .BTF.ext has no records", ext_sec->desc);
+               pr_debug("%s section in .BTF.ext has no records", ext_sec->desc);
                return -EINVAL;
        }
 
@@ -574,14 +655,14 @@ static int btf_ext_copy_info(struct btf_ext *btf_ext,
                __u32 num_records;
 
                if (info_left < sec_hdrlen) {
-                       elog("%s section header is not found in .BTF.ext\n",
+                       pr_debug("%s section header is not found in .BTF.ext\n",
                             ext_sec->desc);
                        return -EINVAL;
                }
 
                num_records = sinfo->num_info;
                if (num_records == 0) {
-                       elog("%s section has incorrect num_records in .BTF.ext\n",
+                       pr_debug("%s section has incorrect num_records in .BTF.ext\n",
                             ext_sec->desc);
                        return -EINVAL;
                }
@@ -589,7 +670,7 @@ static int btf_ext_copy_info(struct btf_ext *btf_ext,
                total_record_size = sec_hdrlen +
                                    (__u64)num_records * record_size;
                if (info_left < total_record_size) {
-                       elog("%s section has incorrect num_records in .BTF.ext\n",
+                       pr_debug("%s section has incorrect num_records in .BTF.ext\n",
                             ext_sec->desc);
                        return -EINVAL;
                }
@@ -601,74 +682,64 @@ static int btf_ext_copy_info(struct btf_ext *btf_ext,
        ext_info = ext_sec->ext_info;
        ext_info->len = ext_sec->len - sizeof(__u32);
        ext_info->rec_size = record_size;
-       ext_info->info = malloc(ext_info->len);
-       if (!ext_info->info)
-               return -ENOMEM;
-       memcpy(ext_info->info, info + sizeof(__u32), ext_info->len);
+       ext_info->info = info + sizeof(__u32);
 
        return 0;
 }
 
-static int btf_ext_copy_func_info(struct btf_ext *btf_ext,
-                                 __u8 *data, __u32 data_size,
-                                 btf_print_fn_t err_log)
+static int btf_ext_setup_func_info(struct btf_ext *btf_ext)
 {
-       const struct btf_ext_header *hdr = (struct btf_ext_header *)data;
-       struct btf_ext_sec_copy_param param = {
-               .off = hdr->func_info_off,
-               .len = hdr->func_info_len,
+       struct btf_ext_sec_setup_param param = {
+               .off = btf_ext->hdr->func_info_off,
+               .len = btf_ext->hdr->func_info_len,
                .min_rec_size = sizeof(struct bpf_func_info_min),
                .ext_info = &btf_ext->func_info,
                .desc = "func_info"
        };
 
-       return btf_ext_copy_info(btf_ext, data, data_size, &param, err_log);
+       return btf_ext_setup_info(btf_ext, &param);
 }
 
-static int btf_ext_copy_line_info(struct btf_ext *btf_ext,
-                                 __u8 *data, __u32 data_size,
-                                 btf_print_fn_t err_log)
+static int btf_ext_setup_line_info(struct btf_ext *btf_ext)
 {
-       const struct btf_ext_header *hdr = (struct btf_ext_header *)data;
-       struct btf_ext_sec_copy_param param = {
-               .off = hdr->line_info_off,
-               .len = hdr->line_info_len,
+       struct btf_ext_sec_setup_param param = {
+               .off = btf_ext->hdr->line_info_off,
+               .len = btf_ext->hdr->line_info_len,
                .min_rec_size = sizeof(struct bpf_line_info_min),
                .ext_info = &btf_ext->line_info,
                .desc = "line_info",
        };
 
-       return btf_ext_copy_info(btf_ext, data, data_size, &param, err_log);
+       return btf_ext_setup_info(btf_ext, &param);
 }
 
-static int btf_ext_parse_hdr(__u8 *data, __u32 data_size,
-                            btf_print_fn_t err_log)
+static int btf_ext_parse_hdr(__u8 *data, __u32 data_size)
 {
        const struct btf_ext_header *hdr = (struct btf_ext_header *)data;
 
        if (data_size < offsetof(struct btf_ext_header, func_info_off) ||
            data_size < hdr->hdr_len) {
-               elog("BTF.ext header not found");
+               pr_debug("BTF.ext header not found");
                return -EINVAL;
        }
 
        if (hdr->magic != BTF_MAGIC) {
-               elog("Invalid BTF.ext magic:%x\n", hdr->magic);
+               pr_debug("Invalid BTF.ext magic:%x\n", hdr->magic);
                return -EINVAL;
        }
 
        if (hdr->version != BTF_VERSION) {
-               elog("Unsupported BTF.ext version:%u\n", hdr->version);
+               pr_debug("Unsupported BTF.ext version:%u\n", hdr->version);
                return -ENOTSUP;
        }
 
        if (hdr->flags) {
-               elog("Unsupported BTF.ext flags:%x\n", hdr->flags);
+               pr_debug("Unsupported BTF.ext flags:%x\n", hdr->flags);
                return -ENOTSUP;
        }
 
        if (data_size == hdr->hdr_len) {
-               elog("BTF.ext has no data\n");
+               pr_debug("BTF.ext has no data\n");
                return -EINVAL;
        }
 
@@ -679,18 +750,16 @@ void btf_ext__free(struct btf_ext *btf_ext)
 {
        if (!btf_ext)
                return;
-
-       free(btf_ext->func_info.info);
-       free(btf_ext->line_info.info);
+       free(btf_ext->data);
        free(btf_ext);
 }
 
-struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log)
+struct btf_ext *btf_ext__new(__u8 *data, __u32 size)
 {
        struct btf_ext *btf_ext;
        int err;
 
-       err = btf_ext_parse_hdr(data, size, err_log);
+       err = btf_ext_parse_hdr(data, size);
        if (err)
                return ERR_PTR(err);
 
@@ -698,13 +767,23 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log)
        if (!btf_ext)
                return ERR_PTR(-ENOMEM);
 
-       err = btf_ext_copy_func_info(btf_ext, data, size, err_log);
-       if (err) {
-               btf_ext__free(btf_ext);
-               return ERR_PTR(err);
+       btf_ext->data_size = size;
+       btf_ext->data = malloc(size);
+       if (!btf_ext->data) {
+               err = -ENOMEM;
+               goto done;
        }
+       memcpy(btf_ext->data, data, size);
+
+       err = btf_ext_setup_func_info(btf_ext);
+       if (err)
+               goto done;
+
+       err = btf_ext_setup_line_info(btf_ext);
+       if (err)
+               goto done;
 
-       err = btf_ext_copy_line_info(btf_ext, data, size, err_log);
+done:
        if (err) {
                btf_ext__free(btf_ext);
                return ERR_PTR(err);
@@ -713,6 +792,12 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log)
        return btf_ext;
 }
 
+const void *btf_ext__get_raw_data(const struct btf_ext *btf_ext, __u32 *size)
+{
+       *size = btf_ext->data_size;
+       return btf_ext->data;
+}
+
 static int btf_ext_reloc_info(const struct btf *btf,
                              const struct btf_ext_info *ext_info,
                              const char *sec_name, __u32 insns_cnt,
@@ -761,7 +846,8 @@ static int btf_ext_reloc_info(const struct btf *btf,
        return -ENOENT;
 }
 
-int btf_ext__reloc_func_info(const struct btf *btf, const struct btf_ext *btf_ext,
+int btf_ext__reloc_func_info(const struct btf *btf,
+                            const struct btf_ext *btf_ext,
                             const char *sec_name, __u32 insns_cnt,
                             void **func_info, __u32 *cnt)
 {
@@ -769,7 +855,8 @@ int btf_ext__reloc_func_info(const struct btf *btf, const struct btf_ext *btf_ex
                                  insns_cnt, func_info, cnt);
 }
 
-int btf_ext__reloc_line_info(const struct btf *btf, const struct btf_ext *btf_ext,
+int btf_ext__reloc_line_info(const struct btf *btf,
+                            const struct btf_ext *btf_ext,
                             const char *sec_name, __u32 insns_cnt,
                             void **line_info, __u32 *cnt)
 {
@@ -786,3 +873,1744 @@ __u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext)
 {
        return btf_ext->line_info.rec_size;
 }
+
+struct btf_dedup;
+
+static struct btf_dedup *btf_dedup_new(struct btf *btf, struct btf_ext *btf_ext,
+                                      const struct btf_dedup_opts *opts);
+static void btf_dedup_free(struct btf_dedup *d);
+static int btf_dedup_strings(struct btf_dedup *d);
+static int btf_dedup_prim_types(struct btf_dedup *d);
+static int btf_dedup_struct_types(struct btf_dedup *d);
+static int btf_dedup_ref_types(struct btf_dedup *d);
+static int btf_dedup_compact_types(struct btf_dedup *d);
+static int btf_dedup_remap_types(struct btf_dedup *d);
+
+/*
+ * Deduplicate BTF types and strings.
+ *
+ * BTF dedup algorithm takes as an input `struct btf` representing `.BTF` ELF
+ * section with all BTF type descriptors and string data. It overwrites that
+ * memory in-place with deduplicated types and strings without any loss of
+ * information. If optional `struct btf_ext` representing '.BTF.ext' ELF section
+ * is provided, all the strings referenced from .BTF.ext section are honored
+ * and updated to point to the right offsets after deduplication.
+ *
+ * If function returns with error, type/string data might be garbled and should
+ * be discarded.
+ *
+ * More verbose and detailed description of both problem btf_dedup is solving,
+ * as well as solution could be found at:
+ * https://facebookmicrosites.github.io/bpf/blog/2018/11/14/btf-enhancement.html
+ *
+ * Problem description and justification
+ * =====================================
+ *
+ * BTF type information is typically emitted either as a result of conversion
+ * from DWARF to BTF or directly by compiler. In both cases, each compilation
+ * unit contains information about a subset of all the types that are used
+ * in an application. These subsets are frequently overlapping and contain a lot
+ * of duplicated information when later concatenated together into a single
+ * binary. This algorithm ensures that each unique type is represented by single
+ * BTF type descriptor, greatly reducing resulting size of BTF data.
+ *
+ * Compilation unit isolation and subsequent duplication of data is not the only
+ * problem. The same type hierarchy (e.g., struct and all the type that struct
+ * references) in different compilation units can be represented in BTF to
+ * various degrees of completeness (or, rather, incompleteness) due to
+ * struct/union forward declarations.
+ *
+ * Let's take a look at an example, that we'll use to better understand the
+ * problem (and solution). Suppose we have two compilation units, each using
+ * same `struct S`, but each of them having incomplete type information about
+ * struct's fields:
+ *
+ * // CU #1:
+ * struct S;
+ * struct A {
+ *     int a;
+ *     struct A* self;
+ *     struct S* parent;
+ * };
+ * struct B;
+ * struct S {
+ *     struct A* a_ptr;
+ *     struct B* b_ptr;
+ * };
+ *
+ * // CU #2:
+ * struct S;
+ * struct A;
+ * struct B {
+ *     int b;
+ *     struct B* self;
+ *     struct S* parent;
+ * };
+ * struct S {
+ *     struct A* a_ptr;
+ *     struct B* b_ptr;
+ * };
+ *
+ * In case of CU #1, BTF data will know only that `struct B` exist (but no
+ * more), but will know the complete type information about `struct A`. While
+ * for CU #2, it will know full type information about `struct B`, but will
+ * only know about forward declaration of `struct A` (in BTF terms, it will
+ * have `BTF_KIND_FWD` type descriptor with name `B`).
+ *
+ * This compilation unit isolation means that it's possible that there is no
+ * single CU with complete type information describing structs `S`, `A`, and
+ * `B`. Also, we might get tons of duplicated and redundant type information.
+ *
+ * Additional complication we need to keep in mind comes from the fact that
+ * types, in general, can form graphs containing cycles, not just DAGs.
+ *
+ * While algorithm does deduplication, it also merges and resolves type
+ * information (unless disabled throught `struct btf_opts`), whenever possible.
+ * E.g., in the example above with two compilation units having partial type
+ * information for structs `A` and `B`, the output of algorithm will emit
+ * a single copy of each BTF type that describes structs `A`, `B`, and `S`
+ * (as well as type information for `int` and pointers), as if they were defined
+ * in a single compilation unit as:
+ *
+ * struct A {
+ *     int a;
+ *     struct A* self;
+ *     struct S* parent;
+ * };
+ * struct B {
+ *     int b;
+ *     struct B* self;
+ *     struct S* parent;
+ * };
+ * struct S {
+ *     struct A* a_ptr;
+ *     struct B* b_ptr;
+ * };
+ *
+ * Algorithm summary
+ * =================
+ *
+ * Algorithm completes its work in 6 separate passes:
+ *
+ * 1. Strings deduplication.
+ * 2. Primitive types deduplication (int, enum, fwd).
+ * 3. Struct/union types deduplication.
+ * 4. Reference types deduplication (pointers, typedefs, arrays, funcs, func
+ *    protos, and const/volatile/restrict modifiers).
+ * 5. Types compaction.
+ * 6. Types remapping.
+ *
+ * Algorithm determines canonical type descriptor, which is a single
+ * representative type for each truly unique type. This canonical type is the
+ * one that will go into final deduplicated BTF type information. For
+ * struct/unions, it is also the type that algorithm will merge additional type
+ * information into (while resolving FWDs), as it discovers it from data in
+ * other CUs. Each input BTF type eventually gets either mapped to itself, if
+ * that type is canonical, or to some other type, if that type is equivalent
+ * and was chosen as canonical representative. This mapping is stored in
+ * `btf_dedup->map` array. This map is also used to record STRUCT/UNION that
+ * FWD type got resolved to.
+ *
+ * To facilitate fast discovery of canonical types, we also maintain canonical
+ * index (`btf_dedup->dedup_table`), which maps type descriptor's signature hash
+ * (i.e., hashed kind, name, size, fields, etc) into a list of canonical types
+ * that match that signature. With sufficiently good choice of type signature
+ * hashing function, we can limit number of canonical types for each unique type
+ * signature to a very small number, allowing to find canonical type for any
+ * duplicated type very quickly.
+ *
+ * Struct/union deduplication is the most critical part and algorithm for
+ * deduplicating structs/unions is described in greater details in comments for
+ * `btf_dedup_is_equiv` function.
+ */
+int btf__dedup(struct btf *btf, struct btf_ext *btf_ext,
+              const struct btf_dedup_opts *opts)
+{
+       struct btf_dedup *d = btf_dedup_new(btf, btf_ext, opts);
+       int err;
+
+       if (IS_ERR(d)) {
+               pr_debug("btf_dedup_new failed: %ld", PTR_ERR(d));
+               return -EINVAL;
+       }
+
+       err = btf_dedup_strings(d);
+       if (err < 0) {
+               pr_debug("btf_dedup_strings failed:%d\n", err);
+               goto done;
+       }
+       err = btf_dedup_prim_types(d);
+       if (err < 0) {
+               pr_debug("btf_dedup_prim_types failed:%d\n", err);
+               goto done;
+       }
+       err = btf_dedup_struct_types(d);
+       if (err < 0) {
+               pr_debug("btf_dedup_struct_types failed:%d\n", err);
+               goto done;
+       }
+       err = btf_dedup_ref_types(d);
+       if (err < 0) {
+               pr_debug("btf_dedup_ref_types failed:%d\n", err);
+               goto done;
+       }
+       err = btf_dedup_compact_types(d);
+       if (err < 0) {
+               pr_debug("btf_dedup_compact_types failed:%d\n", err);
+               goto done;
+       }
+       err = btf_dedup_remap_types(d);
+       if (err < 0) {
+               pr_debug("btf_dedup_remap_types failed:%d\n", err);
+               goto done;
+       }
+
+done:
+       btf_dedup_free(d);
+       return err;
+}
+
+#define BTF_DEDUP_TABLE_SIZE_LOG 14
+#define BTF_DEDUP_TABLE_MOD ((1 << BTF_DEDUP_TABLE_SIZE_LOG) - 1)
+#define BTF_UNPROCESSED_ID ((__u32)-1)
+#define BTF_IN_PROGRESS_ID ((__u32)-2)
+
+struct btf_dedup_node {
+       struct btf_dedup_node *next;
+       __u32 type_id;
+};
+
+struct btf_dedup {
+       /* .BTF section to be deduped in-place */
+       struct btf *btf;
+       /*
+        * Optional .BTF.ext section. When provided, any strings referenced
+        * from it will be taken into account when deduping strings
+        */
+       struct btf_ext *btf_ext;
+       /*
+        * This is a map from any type's signature hash to a list of possible
+        * canonical representative type candidates. Hash collisions are
+        * ignored, so even types of various kinds can share same list of
+        * candidates, which is fine because we rely on subsequent
+        * btf_xxx_equal() checks to authoritatively verify type equality.
+        */
+       struct btf_dedup_node **dedup_table;
+       /* Canonical types map */
+       __u32 *map;
+       /* Hypothetical mapping, used during type graph equivalence checks */
+       __u32 *hypot_map;
+       __u32 *hypot_list;
+       size_t hypot_cnt;
+       size_t hypot_cap;
+       /* Various option modifying behavior of algorithm */
+       struct btf_dedup_opts opts;
+};
+
+struct btf_str_ptr {
+       const char *str;
+       __u32 new_off;
+       bool used;
+};
+
+struct btf_str_ptrs {
+       struct btf_str_ptr *ptrs;
+       const char *data;
+       __u32 cnt;
+       __u32 cap;
+};
+
+static inline __u32 hash_combine(__u32 h, __u32 value)
+{
+/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
+#define GOLDEN_RATIO_PRIME 0x9e370001UL
+       return h * 37 + value * GOLDEN_RATIO_PRIME;
+#undef GOLDEN_RATIO_PRIME
+}
+
+#define for_each_hash_node(table, hash, node) \
+       for (node = table[hash & BTF_DEDUP_TABLE_MOD]; node; node = node->next)
+
+static int btf_dedup_table_add(struct btf_dedup *d, __u32 hash, __u32 type_id)
+{
+       struct btf_dedup_node *node = malloc(sizeof(struct btf_dedup_node));
+
+       if (!node)
+               return -ENOMEM;
+       node->type_id = type_id;
+       node->next = d->dedup_table[hash & BTF_DEDUP_TABLE_MOD];
+       d->dedup_table[hash & BTF_DEDUP_TABLE_MOD] = node;
+       return 0;
+}
+
+static int btf_dedup_hypot_map_add(struct btf_dedup *d,
+                                  __u32 from_id, __u32 to_id)
+{
+       if (d->hypot_cnt == d->hypot_cap) {
+               __u32 *new_list;
+
+               d->hypot_cap += max(16, d->hypot_cap / 2);
+               new_list = realloc(d->hypot_list, sizeof(__u32) * d->hypot_cap);
+               if (!new_list)
+                       return -ENOMEM;
+               d->hypot_list = new_list;
+       }
+       d->hypot_list[d->hypot_cnt++] = from_id;
+       d->hypot_map[from_id] = to_id;
+       return 0;
+}
+
+static void btf_dedup_clear_hypot_map(struct btf_dedup *d)
+{
+       int i;
+
+       for (i = 0; i < d->hypot_cnt; i++)
+               d->hypot_map[d->hypot_list[i]] = BTF_UNPROCESSED_ID;
+       d->hypot_cnt = 0;
+}
+
+static void btf_dedup_table_free(struct btf_dedup *d)
+{
+       struct btf_dedup_node *head, *tmp;
+       int i;
+
+       if (!d->dedup_table)
+               return;
+
+       for (i = 0; i < (1 << BTF_DEDUP_TABLE_SIZE_LOG); i++) {
+               while (d->dedup_table[i]) {
+                       tmp = d->dedup_table[i];
+                       d->dedup_table[i] = tmp->next;
+                       free(tmp);
+               }
+
+               head = d->dedup_table[i];
+               while (head) {
+                       tmp = head;
+                       head = head->next;
+                       free(tmp);
+               }
+       }
+
+       free(d->dedup_table);
+       d->dedup_table = NULL;
+}
+
+static void btf_dedup_free(struct btf_dedup *d)
+{
+       btf_dedup_table_free(d);
+
+       free(d->map);
+       d->map = NULL;
+
+       free(d->hypot_map);
+       d->hypot_map = NULL;
+
+       free(d->hypot_list);
+       d->hypot_list = NULL;
+
+       free(d);
+}
+
+static struct btf_dedup *btf_dedup_new(struct btf *btf, struct btf_ext *btf_ext,
+                                      const struct btf_dedup_opts *opts)
+{
+       struct btf_dedup *d = calloc(1, sizeof(struct btf_dedup));
+       int i, err = 0;
+
+       if (!d)
+               return ERR_PTR(-ENOMEM);
+
+       d->btf = btf;
+       d->btf_ext = btf_ext;
+
+       d->dedup_table = calloc(1 << BTF_DEDUP_TABLE_SIZE_LOG,
+                               sizeof(struct btf_dedup_node *));
+       if (!d->dedup_table) {
+               err = -ENOMEM;
+               goto done;
+       }
+
+       d->map = malloc(sizeof(__u32) * (1 + btf->nr_types));
+       if (!d->map) {
+               err = -ENOMEM;
+               goto done;
+       }
+       /* special BTF "void" type is made canonical immediately */
+       d->map[0] = 0;
+       for (i = 1; i <= btf->nr_types; i++)
+               d->map[i] = BTF_UNPROCESSED_ID;
+
+       d->hypot_map = malloc(sizeof(__u32) * (1 + btf->nr_types));
+       if (!d->hypot_map) {
+               err = -ENOMEM;
+               goto done;
+       }
+       for (i = 0; i <= btf->nr_types; i++)
+               d->hypot_map[i] = BTF_UNPROCESSED_ID;
+
+       d->opts.dont_resolve_fwds = opts && opts->dont_resolve_fwds;
+
+done:
+       if (err) {
+               btf_dedup_free(d);
+               return ERR_PTR(err);
+       }
+
+       return d;
+}
+
+typedef int (*str_off_fn_t)(__u32 *str_off_ptr, void *ctx);
+
+/*
+ * Iterate over all possible places in .BTF and .BTF.ext that can reference
+ * string and pass pointer to it to a provided callback `fn`.
+ */
+static int btf_for_each_str_off(struct btf_dedup *d, str_off_fn_t fn, void *ctx)
+{
+       void *line_data_cur, *line_data_end;
+       int i, j, r, rec_size;
+       struct btf_type *t;
+
+       for (i = 1; i <= d->btf->nr_types; i++) {
+               t = d->btf->types[i];
+               r = fn(&t->name_off, ctx);
+               if (r)
+                       return r;
+
+               switch (BTF_INFO_KIND(t->info)) {
+               case BTF_KIND_STRUCT:
+               case BTF_KIND_UNION: {
+                       struct btf_member *m = (struct btf_member *)(t + 1);
+                       __u16 vlen = BTF_INFO_VLEN(t->info);
+
+                       for (j = 0; j < vlen; j++) {
+                               r = fn(&m->name_off, ctx);
+                               if (r)
+                                       return r;
+                               m++;
+                       }
+                       break;
+               }
+               case BTF_KIND_ENUM: {
+                       struct btf_enum *m = (struct btf_enum *)(t + 1);
+                       __u16 vlen = BTF_INFO_VLEN(t->info);
+
+                       for (j = 0; j < vlen; j++) {
+                               r = fn(&m->name_off, ctx);
+                               if (r)
+                                       return r;
+                               m++;
+                       }
+                       break;
+               }
+               case BTF_KIND_FUNC_PROTO: {
+                       struct btf_param *m = (struct btf_param *)(t + 1);
+                       __u16 vlen = BTF_INFO_VLEN(t->info);
+
+                       for (j = 0; j < vlen; j++) {
+                               r = fn(&m->name_off, ctx);
+                               if (r)
+                                       return r;
+                               m++;
+                       }
+                       break;
+               }
+               default:
+                       break;
+               }
+       }
+
+       if (!d->btf_ext)
+               return 0;
+
+       line_data_cur = d->btf_ext->line_info.info;
+       line_data_end = d->btf_ext->line_info.info + d->btf_ext->line_info.len;
+       rec_size = d->btf_ext->line_info.rec_size;
+
+       while (line_data_cur < line_data_end) {
+               struct btf_ext_info_sec *sec = line_data_cur;
+               struct bpf_line_info_min *line_info;
+               __u32 num_info = sec->num_info;
+
+               r = fn(&sec->sec_name_off, ctx);
+               if (r)
+                       return r;
+
+               line_data_cur += sizeof(struct btf_ext_info_sec);
+               for (i = 0; i < num_info; i++) {
+                       line_info = line_data_cur;
+                       r = fn(&line_info->file_name_off, ctx);
+                       if (r)
+                               return r;
+                       r = fn(&line_info->line_off, ctx);
+                       if (r)
+                               return r;
+                       line_data_cur += rec_size;
+               }
+       }
+
+       return 0;
+}
+
+static int str_sort_by_content(const void *a1, const void *a2)
+{
+       const struct btf_str_ptr *p1 = a1;
+       const struct btf_str_ptr *p2 = a2;
+
+       return strcmp(p1->str, p2->str);
+}
+
+static int str_sort_by_offset(const void *a1, const void *a2)
+{
+       const struct btf_str_ptr *p1 = a1;
+       const struct btf_str_ptr *p2 = a2;
+
+       if (p1->str != p2->str)
+               return p1->str < p2->str ? -1 : 1;
+       return 0;
+}
+
+static int btf_dedup_str_ptr_cmp(const void *str_ptr, const void *pelem)
+{
+       const struct btf_str_ptr *p = pelem;
+
+       if (str_ptr != p->str)
+               return (const char *)str_ptr < p->str ? -1 : 1;
+       return 0;
+}
+
+static int btf_str_mark_as_used(__u32 *str_off_ptr, void *ctx)
+{
+       struct btf_str_ptrs *strs;
+       struct btf_str_ptr *s;
+
+       if (*str_off_ptr == 0)
+               return 0;
+
+       strs = ctx;
+       s = bsearch(strs->data + *str_off_ptr, strs->ptrs, strs->cnt,
+                   sizeof(struct btf_str_ptr), btf_dedup_str_ptr_cmp);
+       if (!s)
+               return -EINVAL;
+       s->used = true;
+       return 0;
+}
+
+static int btf_str_remap_offset(__u32 *str_off_ptr, void *ctx)
+{
+       struct btf_str_ptrs *strs;
+       struct btf_str_ptr *s;
+
+       if (*str_off_ptr == 0)
+               return 0;
+
+       strs = ctx;
+       s = bsearch(strs->data + *str_off_ptr, strs->ptrs, strs->cnt,
+                   sizeof(struct btf_str_ptr), btf_dedup_str_ptr_cmp);
+       if (!s)
+               return -EINVAL;
+       *str_off_ptr = s->new_off;
+       return 0;
+}
+
+/*
+ * Dedup string and filter out those that are not referenced from either .BTF
+ * or .BTF.ext (if provided) sections.
+ *
+ * This is done by building index of all strings in BTF's string section,
+ * then iterating over all entities that can reference strings (e.g., type
+ * names, struct field names, .BTF.ext line info, etc) and marking corresponding
+ * strings as used. After that all used strings are deduped and compacted into
+ * sequential blob of memory and new offsets are calculated. Then all the string
+ * references are iterated again and rewritten using new offsets.
+ */
+static int btf_dedup_strings(struct btf_dedup *d)
+{
+       const struct btf_header *hdr = d->btf->hdr;
+       char *start = (char *)d->btf->nohdr_data + hdr->str_off;
+       char *end = start + d->btf->hdr->str_len;
+       char *p = start, *tmp_strs = NULL;
+       struct btf_str_ptrs strs = {
+               .cnt = 0,
+               .cap = 0,
+               .ptrs = NULL,
+               .data = start,
+       };
+       int i, j, err = 0, grp_idx;
+       bool grp_used;
+
+       /* build index of all strings */
+       while (p < end) {
+               if (strs.cnt + 1 > strs.cap) {
+                       struct btf_str_ptr *new_ptrs;
+
+                       strs.cap += max(strs.cnt / 2, 16);
+                       new_ptrs = realloc(strs.ptrs,
+                                          sizeof(strs.ptrs[0]) * strs.cap);
+                       if (!new_ptrs) {
+                               err = -ENOMEM;
+                               goto done;
+                       }
+                       strs.ptrs = new_ptrs;
+               }
+
+               strs.ptrs[strs.cnt].str = p;
+               strs.ptrs[strs.cnt].used = false;
+
+               p += strlen(p) + 1;
+               strs.cnt++;
+       }
+
+       /* temporary storage for deduplicated strings */
+       tmp_strs = malloc(d->btf->hdr->str_len);
+       if (!tmp_strs) {
+               err = -ENOMEM;
+               goto done;
+       }
+
+       /* mark all used strings */
+       strs.ptrs[0].used = true;
+       err = btf_for_each_str_off(d, btf_str_mark_as_used, &strs);
+       if (err)
+               goto done;
+
+       /* sort strings by context, so that we can identify duplicates */
+       qsort(strs.ptrs, strs.cnt, sizeof(strs.ptrs[0]), str_sort_by_content);
+
+       /*
+        * iterate groups of equal strings and if any instance in a group was
+        * referenced, emit single instance and remember new offset
+        */
+       p = tmp_strs;
+       grp_idx = 0;
+       grp_used = strs.ptrs[0].used;
+       /* iterate past end to avoid code duplication after loop */
+       for (i = 1; i <= strs.cnt; i++) {
+               /*
+                * when i == strs.cnt, we want to skip string comparison and go
+                * straight to handling last group of strings (otherwise we'd
+                * need to handle last group after the loop w/ duplicated code)
+                */
+               if (i < strs.cnt &&
+                   !strcmp(strs.ptrs[i].str, strs.ptrs[grp_idx].str)) {
+                       grp_used = grp_used || strs.ptrs[i].used;
+                       continue;
+               }
+
+               /*
+                * this check would have been required after the loop to handle
+                * last group of strings, but due to <= condition in a loop
+                * we avoid that duplication
+                */
+               if (grp_used) {
+                       int new_off = p - tmp_strs;
+                       __u32 len = strlen(strs.ptrs[grp_idx].str);
+
+                       memmove(p, strs.ptrs[grp_idx].str, len + 1);
+                       for (j = grp_idx; j < i; j++)
+                               strs.ptrs[j].new_off = new_off;
+                       p += len + 1;
+               }
+
+               if (i < strs.cnt) {
+                       grp_idx = i;
+                       grp_used = strs.ptrs[i].used;
+               }
+       }
+
+       /* replace original strings with deduped ones */
+       d->btf->hdr->str_len = p - tmp_strs;
+       memmove(start, tmp_strs, d->btf->hdr->str_len);
+       end = start + d->btf->hdr->str_len;
+
+       /* restore original order for further binary search lookups */
+       qsort(strs.ptrs, strs.cnt, sizeof(strs.ptrs[0]), str_sort_by_offset);
+
+       /* remap string offsets */
+       err = btf_for_each_str_off(d, btf_str_remap_offset, &strs);
+       if (err)
+               goto done;
+
+       d->btf->hdr->str_len = end - start;
+
+done:
+       free(tmp_strs);
+       free(strs.ptrs);
+       return err;
+}
+
+static __u32 btf_hash_common(struct btf_type *t)
+{
+       __u32 h;
+
+       h = hash_combine(0, t->name_off);
+       h = hash_combine(h, t->info);
+       h = hash_combine(h, t->size);
+       return h;
+}
+
+static bool btf_equal_common(struct btf_type *t1, struct btf_type *t2)
+{
+       return t1->name_off == t2->name_off &&
+              t1->info == t2->info &&
+              t1->size == t2->size;
+}
+
+/* Calculate type signature hash of INT. */
+static __u32 btf_hash_int(struct btf_type *t)
+{
+       __u32 info = *(__u32 *)(t + 1);
+       __u32 h;
+
+       h = btf_hash_common(t);
+       h = hash_combine(h, info);
+       return h;
+}
+
+/* Check structural equality of two INTs. */
+static bool btf_equal_int(struct btf_type *t1, struct btf_type *t2)
+{
+       __u32 info1, info2;
+
+       if (!btf_equal_common(t1, t2))
+               return false;
+       info1 = *(__u32 *)(t1 + 1);
+       info2 = *(__u32 *)(t2 + 1);
+       return info1 == info2;
+}
+
+/* Calculate type signature hash of ENUM. */
+static __u32 btf_hash_enum(struct btf_type *t)
+{
+       struct btf_enum *member = (struct btf_enum *)(t + 1);
+       __u32 vlen = BTF_INFO_VLEN(t->info);
+       __u32 h = btf_hash_common(t);
+       int i;
+
+       for (i = 0; i < vlen; i++) {
+               h = hash_combine(h, member->name_off);
+               h = hash_combine(h, member->val);
+               member++;
+       }
+       return h;
+}
+
+/* Check structural equality of two ENUMs. */
+static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
+{
+       struct btf_enum *m1, *m2;
+       __u16 vlen;
+       int i;
+
+       if (!btf_equal_common(t1, t2))
+               return false;
+
+       vlen = BTF_INFO_VLEN(t1->info);
+       m1 = (struct btf_enum *)(t1 + 1);
+       m2 = (struct btf_enum *)(t2 + 1);
+       for (i = 0; i < vlen; i++) {
+               if (m1->name_off != m2->name_off || m1->val != m2->val)
+                       return false;
+               m1++;
+               m2++;
+       }
+       return true;
+}
+
+/*
+ * Calculate type signature hash of STRUCT/UNION, ignoring referenced type IDs,
+ * as referenced type IDs equivalence is established separately during type
+ * graph equivalence check algorithm.
+ */
+static __u32 btf_hash_struct(struct btf_type *t)
+{
+       struct btf_member *member = (struct btf_member *)(t + 1);
+       __u32 vlen = BTF_INFO_VLEN(t->info);
+       __u32 h = btf_hash_common(t);
+       int i;
+
+       for (i = 0; i < vlen; i++) {
+               h = hash_combine(h, member->name_off);
+               h = hash_combine(h, member->offset);
+               /* no hashing of referenced type ID, it can be unresolved yet */
+               member++;
+       }
+       return h;
+}
+
+/*
+ * Check structural compatibility of two FUNC_PROTOs, ignoring referenced type
+ * IDs. This check is performed during type graph equivalence check and
+ * referenced types equivalence is checked separately.
+ */
+static bool btf_equal_struct(struct btf_type *t1, struct btf_type *t2)
+{
+       struct btf_member *m1, *m2;
+       __u16 vlen;
+       int i;
+
+       if (!btf_equal_common(t1, t2))
+               return false;
+
+       vlen = BTF_INFO_VLEN(t1->info);
+       m1 = (struct btf_member *)(t1 + 1);
+       m2 = (struct btf_member *)(t2 + 1);
+       for (i = 0; i < vlen; i++) {
+               if (m1->name_off != m2->name_off || m1->offset != m2->offset)
+                       return false;
+               m1++;
+               m2++;
+       }
+       return true;
+}
+
+/*
+ * Calculate type signature hash of ARRAY, including referenced type IDs,
+ * under assumption that they were already resolved to canonical type IDs and
+ * are not going to change.
+ */
+static __u32 btf_hash_array(struct btf_type *t)
+{
+       struct btf_array *info = (struct btf_array *)(t + 1);
+       __u32 h = btf_hash_common(t);
+
+       h = hash_combine(h, info->type);
+       h = hash_combine(h, info->index_type);
+       h = hash_combine(h, info->nelems);
+       return h;
+}
+
+/*
+ * Check exact equality of two ARRAYs, taking into account referenced
+ * type IDs, under assumption that they were already resolved to canonical
+ * type IDs and are not going to change.
+ * This function is called during reference types deduplication to compare
+ * ARRAY to potential canonical representative.
+ */
+static bool btf_equal_array(struct btf_type *t1, struct btf_type *t2)
+{
+       struct btf_array *info1, *info2;
+
+       if (!btf_equal_common(t1, t2))
+               return false;
+
+       info1 = (struct btf_array *)(t1 + 1);
+       info2 = (struct btf_array *)(t2 + 1);
+       return info1->type == info2->type &&
+              info1->index_type == info2->index_type &&
+              info1->nelems == info2->nelems;
+}
+
+/*
+ * Check structural compatibility of two ARRAYs, ignoring referenced type
+ * IDs. This check is performed during type graph equivalence check and
+ * referenced types equivalence is checked separately.
+ */
+static bool btf_compat_array(struct btf_type *t1, struct btf_type *t2)
+{
+       struct btf_array *info1, *info2;
+
+       if (!btf_equal_common(t1, t2))
+               return false;
+
+       info1 = (struct btf_array *)(t1 + 1);
+       info2 = (struct btf_array *)(t2 + 1);
+       return info1->nelems == info2->nelems;
+}
+
+/*
+ * Calculate type signature hash of FUNC_PROTO, including referenced type IDs,
+ * under assumption that they were already resolved to canonical type IDs and
+ * are not going to change.
+ */
+static inline __u32 btf_hash_fnproto(struct btf_type *t)
+{
+       struct btf_param *member = (struct btf_param *)(t + 1);
+       __u16 vlen = BTF_INFO_VLEN(t->info);
+       __u32 h = btf_hash_common(t);
+       int i;
+
+       for (i = 0; i < vlen; i++) {
+               h = hash_combine(h, member->name_off);
+               h = hash_combine(h, member->type);
+               member++;
+       }
+       return h;
+}
+
+/*
+ * Check exact equality of two FUNC_PROTOs, taking into account referenced
+ * type IDs, under assumption that they were already resolved to canonical
+ * type IDs and are not going to change.
+ * This function is called during reference types deduplication to compare
+ * FUNC_PROTO to potential canonical representative.
+ */
+static inline bool btf_equal_fnproto(struct btf_type *t1, struct btf_type *t2)
+{
+       struct btf_param *m1, *m2;
+       __u16 vlen;
+       int i;
+
+       if (!btf_equal_common(t1, t2))
+               return false;
+
+       vlen = BTF_INFO_VLEN(t1->info);
+       m1 = (struct btf_param *)(t1 + 1);
+       m2 = (struct btf_param *)(t2 + 1);
+       for (i = 0; i < vlen; i++) {
+               if (m1->name_off != m2->name_off || m1->type != m2->type)
+                       return false;
+               m1++;
+               m2++;
+       }
+       return true;
+}
+
+/*
+ * Check structural compatibility of two FUNC_PROTOs, ignoring referenced type
+ * IDs. This check is performed during type graph equivalence check and
+ * referenced types equivalence is checked separately.
+ */
+static inline bool btf_compat_fnproto(struct btf_type *t1, struct btf_type *t2)
+{
+       struct btf_param *m1, *m2;
+       __u16 vlen;
+       int i;
+
+       /* skip return type ID */
+       if (t1->name_off != t2->name_off || t1->info != t2->info)
+               return false;
+
+       vlen = BTF_INFO_VLEN(t1->info);
+       m1 = (struct btf_param *)(t1 + 1);
+       m2 = (struct btf_param *)(t2 + 1);
+       for (i = 0; i < vlen; i++) {
+               if (m1->name_off != m2->name_off)
+                       return false;
+               m1++;
+               m2++;
+       }
+       return true;
+}
+
+/*
+ * Deduplicate primitive types, that can't reference other types, by calculating
+ * their type signature hash and comparing them with any possible canonical
+ * candidate. If no canonical candidate matches, type itself is marked as
+ * canonical and is added into `btf_dedup->dedup_table` as another candidate.
+ */
+static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
+{
+       struct btf_type *t = d->btf->types[type_id];
+       struct btf_type *cand;
+       struct btf_dedup_node *cand_node;
+       /* if we don't find equivalent type, then we are canonical */
+       __u32 new_id = type_id;
+       __u32 h;
+
+       switch (BTF_INFO_KIND(t->info)) {
+       case BTF_KIND_CONST:
+       case BTF_KIND_VOLATILE:
+       case BTF_KIND_RESTRICT:
+       case BTF_KIND_PTR:
+       case BTF_KIND_TYPEDEF:
+       case BTF_KIND_ARRAY:
+       case BTF_KIND_STRUCT:
+       case BTF_KIND_UNION:
+       case BTF_KIND_FUNC:
+       case BTF_KIND_FUNC_PROTO:
+               return 0;
+
+       case BTF_KIND_INT:
+               h = btf_hash_int(t);
+               for_each_hash_node(d->dedup_table, h, cand_node) {
+                       cand = d->btf->types[cand_node->type_id];
+                       if (btf_equal_int(t, cand)) {
+                               new_id = cand_node->type_id;
+                               break;
+                       }
+               }
+               break;
+
+       case BTF_KIND_ENUM:
+               h = btf_hash_enum(t);
+               for_each_hash_node(d->dedup_table, h, cand_node) {
+                       cand = d->btf->types[cand_node->type_id];
+                       if (btf_equal_enum(t, cand)) {
+                               new_id = cand_node->type_id;
+                               break;
+                       }
+               }
+               break;
+
+       case BTF_KIND_FWD:
+               h = btf_hash_common(t);
+               for_each_hash_node(d->dedup_table, h, cand_node) {
+                       cand = d->btf->types[cand_node->type_id];
+                       if (btf_equal_common(t, cand)) {
+                               new_id = cand_node->type_id;
+                               break;
+                       }
+               }
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       d->map[type_id] = new_id;
+       if (type_id == new_id && btf_dedup_table_add(d, h, type_id))
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int btf_dedup_prim_types(struct btf_dedup *d)
+{
+       int i, err;
+
+       for (i = 1; i <= d->btf->nr_types; i++) {
+               err = btf_dedup_prim_type(d, i);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+/*
+ * Check whether type is already mapped into canonical one (could be to itself).
+ */
+static inline bool is_type_mapped(struct btf_dedup *d, uint32_t type_id)
+{
+       return d->map[type_id] <= BTF_MAX_NR_TYPES;
+}
+
+/*
+ * Resolve type ID into its canonical type ID, if any; otherwise return original
+ * type ID. If type is FWD and is resolved into STRUCT/UNION already, follow
+ * STRUCT/UNION link and resolve it into canonical type ID as well.
+ */
+static inline __u32 resolve_type_id(struct btf_dedup *d, __u32 type_id)
+{
+       while (is_type_mapped(d, type_id) && d->map[type_id] != type_id)
+               type_id = d->map[type_id];
+       return type_id;
+}
+
+/*
+ * Resolve FWD to underlying STRUCT/UNION, if any; otherwise return original
+ * type ID.
+ */
+static uint32_t resolve_fwd_id(struct btf_dedup *d, uint32_t type_id)
+{
+       __u32 orig_type_id = type_id;
+
+       if (BTF_INFO_KIND(d->btf->types[type_id]->info) != BTF_KIND_FWD)
+               return type_id;
+
+       while (is_type_mapped(d, type_id) && d->map[type_id] != type_id)
+               type_id = d->map[type_id];
+
+       if (BTF_INFO_KIND(d->btf->types[type_id]->info) != BTF_KIND_FWD)
+               return type_id;
+
+       return orig_type_id;
+}
+
+
+static inline __u16 btf_fwd_kind(struct btf_type *t)
+{
+       return BTF_INFO_KFLAG(t->info) ? BTF_KIND_UNION : BTF_KIND_STRUCT;
+}
+
+/*
+ * Check equivalence of BTF type graph formed by candidate struct/union (we'll
+ * call it "candidate graph" in this description for brevity) to a type graph
+ * formed by (potential) canonical struct/union ("canonical graph" for brevity
+ * here, though keep in mind that not all types in canonical graph are
+ * necessarily canonical representatives themselves, some of them might be
+ * duplicates or its uniqueness might not have been established yet).
+ * Returns:
+ *  - >0, if type graphs are equivalent;
+ *  -  0, if not equivalent;
+ *  - <0, on error.
+ *
+ * Algorithm performs side-by-side DFS traversal of both type graphs and checks
+ * equivalence of BTF types at each step. If at any point BTF types in candidate
+ * and canonical graphs are not compatible structurally, whole graphs are
+ * incompatible. If types are structurally equivalent (i.e., all information
+ * except referenced type IDs is exactly the same), a mapping from `canon_id` to
+ * a `cand_id` is recored in hypothetical mapping (`btf_dedup->hypot_map`).
+ * If a type references other types, then those referenced types are checked
+ * for equivalence recursively.
+ *
+ * During DFS traversal, if we find that for current `canon_id` type we
+ * already have some mapping in hypothetical map, we check for two possible
+ * situations:
+ *   - `canon_id` is mapped to exactly the same type as `cand_id`. This will
+ *     happen when type graphs have cycles. In this case we assume those two
+ *     types are equivalent.
+ *   - `canon_id` is mapped to different type. This is contradiction in our
+ *     hypothetical mapping, because same graph in canonical graph corresponds
+ *     to two different types in candidate graph, which for equivalent type
+ *     graphs shouldn't happen. This condition terminates equivalence check
+ *     with negative result.
+ *
+ * If type graphs traversal exhausts types to check and find no contradiction,
+ * then type graphs are equivalent.
+ *
+ * When checking types for equivalence, there is one special case: FWD types.
+ * If FWD type resolution is allowed and one of the types (either from canonical
+ * or candidate graph) is FWD and other is STRUCT/UNION (depending on FWD's kind
+ * flag) and their names match, hypothetical mapping is updated to point from
+ * FWD to STRUCT/UNION. If graphs will be determined as equivalent successfully,
+ * this mapping will be used to record FWD -> STRUCT/UNION mapping permanently.
+ *
+ * Technically, this could lead to incorrect FWD to STRUCT/UNION resolution,
+ * if there are two exactly named (or anonymous) structs/unions that are
+ * compatible structurally, one of which has FWD field, while other is concrete
+ * STRUCT/UNION, but according to C sources they are different structs/unions
+ * that are referencing different types with the same name. This is extremely
+ * unlikely to happen, but btf_dedup API allows to disable FWD resolution if
+ * this logic is causing problems.
+ *
+ * Doing FWD resolution means that both candidate and/or canonical graphs can
+ * consists of portions of the graph that come from multiple compilation units.
+ * This is due to the fact that types within single compilation unit are always
+ * deduplicated and FWDs are already resolved, if referenced struct/union
+ * definiton is available. So, if we had unresolved FWD and found corresponding
+ * STRUCT/UNION, they will be from different compilation units. This
+ * consequently means that when we "link" FWD to corresponding STRUCT/UNION,
+ * type graph will likely have at least two different BTF types that describe
+ * same type (e.g., most probably there will be two different BTF types for the
+ * same 'int' primitive type) and could even have "overlapping" parts of type
+ * graph that describe same subset of types.
+ *
+ * This in turn means that our assumption that each type in canonical graph
+ * must correspond to exactly one type in candidate graph might not hold
+ * anymore and will make it harder to detect contradictions using hypothetical
+ * map. To handle this problem, we allow to follow FWD -> STRUCT/UNION
+ * resolution only in canonical graph. FWDs in candidate graphs are never
+ * resolved. To see why it's OK, let's check all possible situations w.r.t. FWDs
+ * that can occur:
+ *   - Both types in canonical and candidate graphs are FWDs. If they are
+ *     structurally equivalent, then they can either be both resolved to the
+ *     same STRUCT/UNION or not resolved at all. In both cases they are
+ *     equivalent and there is no need to resolve FWD on candidate side.
+ *   - Both types in canonical and candidate graphs are concrete STRUCT/UNION,
+ *     so nothing to resolve as well, algorithm will check equivalence anyway.
+ *   - Type in canonical graph is FWD, while type in candidate is concrete
+ *     STRUCT/UNION. In this case candidate graph comes from single compilation
+ *     unit, so there is exactly one BTF type for each unique C type. After
+ *     resolving FWD into STRUCT/UNION, there might be more than one BTF type
+ *     in canonical graph mapping to single BTF type in candidate graph, but
+ *     because hypothetical mapping maps from canonical to candidate types, it's
+ *     alright, and we still maintain the property of having single `canon_id`
+ *     mapping to single `cand_id` (there could be two different `canon_id`
+ *     mapped to the same `cand_id`, but it's not contradictory).
+ *   - Type in canonical graph is concrete STRUCT/UNION, while type in candidate
+ *     graph is FWD. In this case we are just going to check compatibility of
+ *     STRUCT/UNION and corresponding FWD, and if they are compatible, we'll
+ *     assume that whatever STRUCT/UNION FWD resolves to must be equivalent to
+ *     a concrete STRUCT/UNION from canonical graph. If the rest of type graphs
+ *     turn out equivalent, we'll re-resolve FWD to concrete STRUCT/UNION from
+ *     canonical graph.
+ */
+static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
+                             __u32 canon_id)
+{
+       struct btf_type *cand_type;
+       struct btf_type *canon_type;
+       __u32 hypot_type_id;
+       __u16 cand_kind;
+       __u16 canon_kind;
+       int i, eq;
+
+       /* if both resolve to the same canonical, they must be equivalent */
+       if (resolve_type_id(d, cand_id) == resolve_type_id(d, canon_id))
+               return 1;
+
+       canon_id = resolve_fwd_id(d, canon_id);
+
+       hypot_type_id = d->hypot_map[canon_id];
+       if (hypot_type_id <= BTF_MAX_NR_TYPES)
+               return hypot_type_id == cand_id;
+
+       if (btf_dedup_hypot_map_add(d, canon_id, cand_id))
+               return -ENOMEM;
+
+       cand_type = d->btf->types[cand_id];
+       canon_type = d->btf->types[canon_id];
+       cand_kind = BTF_INFO_KIND(cand_type->info);
+       canon_kind = BTF_INFO_KIND(canon_type->info);
+
+       if (cand_type->name_off != canon_type->name_off)
+               return 0;
+
+       /* FWD <--> STRUCT/UNION equivalence check, if enabled */
+       if (!d->opts.dont_resolve_fwds
+           && (cand_kind == BTF_KIND_FWD || canon_kind == BTF_KIND_FWD)
+           && cand_kind != canon_kind) {
+               __u16 real_kind;
+               __u16 fwd_kind;
+
+               if (cand_kind == BTF_KIND_FWD) {
+                       real_kind = canon_kind;
+                       fwd_kind = btf_fwd_kind(cand_type);
+               } else {
+                       real_kind = cand_kind;
+                       fwd_kind = btf_fwd_kind(canon_type);
+               }
+               return fwd_kind == real_kind;
+       }
+
+       if (cand_type->info != canon_type->info)
+               return 0;
+
+       switch (cand_kind) {
+       case BTF_KIND_INT:
+               return btf_equal_int(cand_type, canon_type);
+
+       case BTF_KIND_ENUM:
+               return btf_equal_enum(cand_type, canon_type);
+
+       case BTF_KIND_FWD:
+               return btf_equal_common(cand_type, canon_type);
+
+       case BTF_KIND_CONST:
+       case BTF_KIND_VOLATILE:
+       case BTF_KIND_RESTRICT:
+       case BTF_KIND_PTR:
+       case BTF_KIND_TYPEDEF:
+       case BTF_KIND_FUNC:
+               return btf_dedup_is_equiv(d, cand_type->type, canon_type->type);
+
+       case BTF_KIND_ARRAY: {
+               struct btf_array *cand_arr, *canon_arr;
+
+               if (!btf_compat_array(cand_type, canon_type))
+                       return 0;
+               cand_arr = (struct btf_array *)(cand_type + 1);
+               canon_arr = (struct btf_array *)(canon_type + 1);
+               eq = btf_dedup_is_equiv(d,
+                       cand_arr->index_type, canon_arr->index_type);
+               if (eq <= 0)
+                       return eq;
+               return btf_dedup_is_equiv(d, cand_arr->type, canon_arr->type);
+       }
+
+       case BTF_KIND_STRUCT:
+       case BTF_KIND_UNION: {
+               struct btf_member *cand_m, *canon_m;
+               __u16 vlen;
+
+               if (!btf_equal_struct(cand_type, canon_type))
+                       return 0;
+               vlen = BTF_INFO_VLEN(cand_type->info);
+               cand_m = (struct btf_member *)(cand_type + 1);
+               canon_m = (struct btf_member *)(canon_type + 1);
+               for (i = 0; i < vlen; i++) {
+                       eq = btf_dedup_is_equiv(d, cand_m->type, canon_m->type);
+                       if (eq <= 0)
+                               return eq;
+                       cand_m++;
+                       canon_m++;
+               }
+
+               return 1;
+       }
+
+       case BTF_KIND_FUNC_PROTO: {
+               struct btf_param *cand_p, *canon_p;
+               __u16 vlen;
+
+               if (!btf_compat_fnproto(cand_type, canon_type))
+                       return 0;
+               eq = btf_dedup_is_equiv(d, cand_type->type, canon_type->type);
+               if (eq <= 0)
+                       return eq;
+               vlen = BTF_INFO_VLEN(cand_type->info);
+               cand_p = (struct btf_param *)(cand_type + 1);
+               canon_p = (struct btf_param *)(canon_type + 1);
+               for (i = 0; i < vlen; i++) {
+                       eq = btf_dedup_is_equiv(d, cand_p->type, canon_p->type);
+                       if (eq <= 0)
+                               return eq;
+                       cand_p++;
+                       canon_p++;
+               }
+               return 1;
+       }
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*
+ * Use hypothetical mapping, produced by successful type graph equivalence
+ * check, to augment existing struct/union canonical mapping, where possible.
+ *
+ * If BTF_KIND_FWD resolution is allowed, this mapping is also used to record
+ * FWD -> STRUCT/UNION correspondence as well. FWD resolution is bidirectional:
+ * it doesn't matter if FWD type was part of canonical graph or candidate one,
+ * we are recording the mapping anyway. As opposed to carefulness required
+ * for struct/union correspondence mapping (described below), for FWD resolution
+ * it's not important, as by the time that FWD type (reference type) will be
+ * deduplicated all structs/unions will be deduped already anyway.
+ *
+ * Recording STRUCT/UNION mapping is purely a performance optimization and is
+ * not required for correctness. It needs to be done carefully to ensure that
+ * struct/union from candidate's type graph is not mapped into corresponding
+ * struct/union from canonical type graph that itself hasn't been resolved into
+ * canonical representative. The only guarantee we have is that canonical
+ * struct/union was determined as canonical and that won't change. But any
+ * types referenced through that struct/union fields could have been not yet
+ * resolved, so in case like that it's too early to establish any kind of
+ * correspondence between structs/unions.
+ *
+ * No canonical correspondence is derived for primitive types (they are already
+ * deduplicated completely already anyway) or reference types (they rely on
+ * stability of struct/union canonical relationship for equivalence checks).
+ */
+static void btf_dedup_merge_hypot_map(struct btf_dedup *d)
+{
+       __u32 cand_type_id, targ_type_id;
+       __u16 t_kind, c_kind;
+       __u32 t_id, c_id;
+       int i;
+
+       for (i = 0; i < d->hypot_cnt; i++) {
+               cand_type_id = d->hypot_list[i];
+               targ_type_id = d->hypot_map[cand_type_id];
+               t_id = resolve_type_id(d, targ_type_id);
+               c_id = resolve_type_id(d, cand_type_id);
+               t_kind = BTF_INFO_KIND(d->btf->types[t_id]->info);
+               c_kind = BTF_INFO_KIND(d->btf->types[c_id]->info);
+               /*
+                * Resolve FWD into STRUCT/UNION.
+                * It's ok to resolve FWD into STRUCT/UNION that's not yet
+                * mapped to canonical representative (as opposed to
+                * STRUCT/UNION <--> STRUCT/UNION mapping logic below), because
+                * eventually that struct is going to be mapped and all resolved
+                * FWDs will automatically resolve to correct canonical
+                * representative. This will happen before ref type deduping,
+                * which critically depends on stability of these mapping. This
+                * stability is not a requirement for STRUCT/UNION equivalence
+                * checks, though.
+                */
+               if (t_kind != BTF_KIND_FWD && c_kind == BTF_KIND_FWD)
+                       d->map[c_id] = t_id;
+               else if (t_kind == BTF_KIND_FWD && c_kind != BTF_KIND_FWD)
+                       d->map[t_id] = c_id;
+
+               if ((t_kind == BTF_KIND_STRUCT || t_kind == BTF_KIND_UNION) &&
+                   c_kind != BTF_KIND_FWD &&
+                   is_type_mapped(d, c_id) &&
+                   !is_type_mapped(d, t_id)) {
+                       /*
+                        * as a perf optimization, we can map struct/union
+                        * that's part of type graph we just verified for
+                        * equivalence. We can do that for struct/union that has
+                        * canonical representative only, though.
+                        */
+                       d->map[t_id] = c_id;
+               }
+       }
+}
+
+/*
+ * Deduplicate struct/union types.
+ *
+ * For each struct/union type its type signature hash is calculated, taking
+ * into account type's name, size, number, order and names of fields, but
+ * ignoring type ID's referenced from fields, because they might not be deduped
+ * completely until after reference types deduplication phase. This type hash
+ * is used to iterate over all potential canonical types, sharing same hash.
+ * For each canonical candidate we check whether type graphs that they form
+ * (through referenced types in fields and so on) are equivalent using algorithm
+ * implemented in `btf_dedup_is_equiv`. If such equivalence is found and
+ * BTF_KIND_FWD resolution is allowed, then hypothetical mapping
+ * (btf_dedup->hypot_map) produced by aforementioned type graph equivalence
+ * algorithm is used to record FWD -> STRUCT/UNION mapping. It's also used to
+ * potentially map other structs/unions to their canonical representatives,
+ * if such relationship hasn't yet been established. This speeds up algorithm
+ * by eliminating some of the duplicate work.
+ *
+ * If no matching canonical representative was found, struct/union is marked
+ * as canonical for itself and is added into btf_dedup->dedup_table hash map
+ * for further look ups.
+ */
+static int btf_dedup_struct_type(struct btf_dedup *d, __u32 type_id)
+{
+       struct btf_dedup_node *cand_node;
+       struct btf_type *t;
+       /* if we don't find equivalent type, then we are canonical */
+       __u32 new_id = type_id;
+       __u16 kind;
+       __u32 h;
+
+       /* already deduped or is in process of deduping (loop detected) */
+       if (d->map[type_id] <= BTF_MAX_NR_TYPES)
+               return 0;
+
+       t = d->btf->types[type_id];
+       kind = BTF_INFO_KIND(t->info);
+
+       if (kind != BTF_KIND_STRUCT && kind != BTF_KIND_UNION)
+               return 0;
+
+       h = btf_hash_struct(t);
+       for_each_hash_node(d->dedup_table, h, cand_node) {
+               int eq;
+
+               btf_dedup_clear_hypot_map(d);
+               eq = btf_dedup_is_equiv(d, type_id, cand_node->type_id);
+               if (eq < 0)
+                       return eq;
+               if (!eq)
+                       continue;
+               new_id = cand_node->type_id;
+               btf_dedup_merge_hypot_map(d);
+               break;
+       }
+
+       d->map[type_id] = new_id;
+       if (type_id == new_id && btf_dedup_table_add(d, h, type_id))
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int btf_dedup_struct_types(struct btf_dedup *d)
+{
+       int i, err;
+
+       for (i = 1; i <= d->btf->nr_types; i++) {
+               err = btf_dedup_struct_type(d, i);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+/*
+ * Deduplicate reference type.
+ *
+ * Once all primitive and struct/union types got deduplicated, we can easily
+ * deduplicate all other (reference) BTF types. This is done in two steps:
+ *
+ * 1. Resolve all referenced type IDs into their canonical type IDs. This
+ * resolution can be done either immediately for primitive or struct/union types
+ * (because they were deduped in previous two phases) or recursively for
+ * reference types. Recursion will always terminate at either primitive or
+ * struct/union type, at which point we can "unwind" chain of reference types
+ * one by one. There is no danger of encountering cycles because in C type
+ * system the only way to form type cycle is through struct/union, so any chain
+ * of reference types, even those taking part in a type cycle, will inevitably
+ * reach struct/union at some point.
+ *
+ * 2. Once all referenced type IDs are resolved into canonical ones, BTF type
+ * becomes "stable", in the sense that no further deduplication will cause
+ * any changes to it. With that, it's now possible to calculate type's signature
+ * hash (this time taking into account referenced type IDs) and loop over all
+ * potential canonical representatives. If no match was found, current type
+ * will become canonical representative of itself and will be added into
+ * btf_dedup->dedup_table as another possible canonical representative.
+ */
+static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
+{
+       struct btf_dedup_node *cand_node;
+       struct btf_type *t, *cand;
+       /* if we don't find equivalent type, then we are representative type */
+       __u32 new_id = type_id;
+       __u32 h, ref_type_id;
+
+       if (d->map[type_id] == BTF_IN_PROGRESS_ID)
+               return -ELOOP;
+       if (d->map[type_id] <= BTF_MAX_NR_TYPES)
+               return resolve_type_id(d, type_id);
+
+       t = d->btf->types[type_id];
+       d->map[type_id] = BTF_IN_PROGRESS_ID;
+
+       switch (BTF_INFO_KIND(t->info)) {
+       case BTF_KIND_CONST:
+       case BTF_KIND_VOLATILE:
+       case BTF_KIND_RESTRICT:
+       case BTF_KIND_PTR:
+       case BTF_KIND_TYPEDEF:
+       case BTF_KIND_FUNC:
+               ref_type_id = btf_dedup_ref_type(d, t->type);
+               if (ref_type_id < 0)
+                       return ref_type_id;
+               t->type = ref_type_id;
+
+               h = btf_hash_common(t);
+               for_each_hash_node(d->dedup_table, h, cand_node) {
+                       cand = d->btf->types[cand_node->type_id];
+                       if (btf_equal_common(t, cand)) {
+                               new_id = cand_node->type_id;
+                               break;
+                       }
+               }
+               break;
+
+       case BTF_KIND_ARRAY: {
+               struct btf_array *info = (struct btf_array *)(t + 1);
+
+               ref_type_id = btf_dedup_ref_type(d, info->type);
+               if (ref_type_id < 0)
+                       return ref_type_id;
+               info->type = ref_type_id;
+
+               ref_type_id = btf_dedup_ref_type(d, info->index_type);
+               if (ref_type_id < 0)
+                       return ref_type_id;
+               info->index_type = ref_type_id;
+
+               h = btf_hash_array(t);
+               for_each_hash_node(d->dedup_table, h, cand_node) {
+                       cand = d->btf->types[cand_node->type_id];
+                       if (btf_equal_array(t, cand)) {
+                               new_id = cand_node->type_id;
+                               break;
+                       }
+               }
+               break;
+       }
+
+       case BTF_KIND_FUNC_PROTO: {
+               struct btf_param *param;
+               __u16 vlen;
+               int i;
+
+               ref_type_id = btf_dedup_ref_type(d, t->type);
+               if (ref_type_id < 0)
+                       return ref_type_id;
+               t->type = ref_type_id;
+
+               vlen = BTF_INFO_VLEN(t->info);
+               param = (struct btf_param *)(t + 1);
+               for (i = 0; i < vlen; i++) {
+                       ref_type_id = btf_dedup_ref_type(d, param->type);
+                       if (ref_type_id < 0)
+                               return ref_type_id;
+                       param->type = ref_type_id;
+                       param++;
+               }
+
+               h = btf_hash_fnproto(t);
+               for_each_hash_node(d->dedup_table, h, cand_node) {
+                       cand = d->btf->types[cand_node->type_id];
+                       if (btf_equal_fnproto(t, cand)) {
+                               new_id = cand_node->type_id;
+                               break;
+                       }
+               }
+               break;
+       }
+
+       default:
+               return -EINVAL;
+       }
+
+       d->map[type_id] = new_id;
+       if (type_id == new_id && btf_dedup_table_add(d, h, type_id))
+               return -ENOMEM;
+
+       return new_id;
+}
+
+static int btf_dedup_ref_types(struct btf_dedup *d)
+{
+       int i, err;
+
+       for (i = 1; i <= d->btf->nr_types; i++) {
+               err = btf_dedup_ref_type(d, i);
+               if (err < 0)
+                       return err;
+       }
+       btf_dedup_table_free(d);
+       return 0;
+}
+
+/*
+ * Compact types.
+ *
+ * After we established for each type its corresponding canonical representative
+ * type, we now can eliminate types that are not canonical and leave only
+ * canonical ones layed out sequentially in memory by copying them over
+ * duplicates. During compaction btf_dedup->hypot_map array is reused to store
+ * a map from original type ID to a new compacted type ID, which will be used
+ * during next phase to "fix up" type IDs, referenced from struct/union and
+ * reference types.
+ */
+static int btf_dedup_compact_types(struct btf_dedup *d)
+{
+       struct btf_type **new_types;
+       __u32 next_type_id = 1;
+       char *types_start, *p;
+       int i, len;
+
+       /* we are going to reuse hypot_map to store compaction remapping */
+       d->hypot_map[0] = 0;
+       for (i = 1; i <= d->btf->nr_types; i++)
+               d->hypot_map[i] = BTF_UNPROCESSED_ID;
+
+       types_start = d->btf->nohdr_data + d->btf->hdr->type_off;
+       p = types_start;
+
+       for (i = 1; i <= d->btf->nr_types; i++) {
+               if (d->map[i] != i)
+                       continue;
+
+               len = btf_type_size(d->btf->types[i]);
+               if (len < 0)
+                       return len;
+
+               memmove(p, d->btf->types[i], len);
+               d->hypot_map[i] = next_type_id;
+               d->btf->types[next_type_id] = (struct btf_type *)p;
+               p += len;
+               next_type_id++;
+       }
+
+       /* shrink struct btf's internal types index and update btf_header */
+       d->btf->nr_types = next_type_id - 1;
+       d->btf->types_size = d->btf->nr_types;
+       d->btf->hdr->type_len = p - types_start;
+       new_types = realloc(d->btf->types,
+                           (1 + d->btf->nr_types) * sizeof(struct btf_type *));
+       if (!new_types)
+               return -ENOMEM;
+       d->btf->types = new_types;
+
+       /* make sure string section follows type information without gaps */
+       d->btf->hdr->str_off = p - (char *)d->btf->nohdr_data;
+       memmove(p, d->btf->strings, d->btf->hdr->str_len);
+       d->btf->strings = p;
+       p += d->btf->hdr->str_len;
+
+       d->btf->data_size = p - (char *)d->btf->data;
+       return 0;
+}
+
+/*
+ * Figure out final (deduplicated and compacted) type ID for provided original
+ * `type_id` by first resolving it into corresponding canonical type ID and
+ * then mapping it to a deduplicated type ID, stored in btf_dedup->hypot_map,
+ * which is populated during compaction phase.
+ */
+static int btf_dedup_remap_type_id(struct btf_dedup *d, __u32 type_id)
+{
+       __u32 resolved_type_id, new_type_id;
+
+       resolved_type_id = resolve_type_id(d, type_id);
+       new_type_id = d->hypot_map[resolved_type_id];
+       if (new_type_id > BTF_MAX_NR_TYPES)
+               return -EINVAL;
+       return new_type_id;
+}
+
+/*
+ * Remap referenced type IDs into deduped type IDs.
+ *
+ * After BTF types are deduplicated and compacted, their final type IDs may
+ * differ from original ones. The map from original to a corresponding
+ * deduped type ID is stored in btf_dedup->hypot_map and is populated during
+ * compaction phase. During remapping phase we are rewriting all type IDs
+ * referenced from any BTF type (e.g., struct fields, func proto args, etc) to
+ * their final deduped type IDs.
+ */
+static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
+{
+       struct btf_type *t = d->btf->types[type_id];
+       int i, r;
+
+       switch (BTF_INFO_KIND(t->info)) {
+       case BTF_KIND_INT:
+       case BTF_KIND_ENUM:
+               break;
+
+       case BTF_KIND_FWD:
+       case BTF_KIND_CONST:
+       case BTF_KIND_VOLATILE:
+       case BTF_KIND_RESTRICT:
+       case BTF_KIND_PTR:
+       case BTF_KIND_TYPEDEF:
+       case BTF_KIND_FUNC:
+               r = btf_dedup_remap_type_id(d, t->type);
+               if (r < 0)
+                       return r;
+               t->type = r;
+               break;
+
+       case BTF_KIND_ARRAY: {
+               struct btf_array *arr_info = (struct btf_array *)(t + 1);
+
+               r = btf_dedup_remap_type_id(d, arr_info->type);
+               if (r < 0)
+                       return r;
+               arr_info->type = r;
+               r = btf_dedup_remap_type_id(d, arr_info->index_type);
+               if (r < 0)
+                       return r;
+               arr_info->index_type = r;
+               break;
+       }
+
+       case BTF_KIND_STRUCT:
+       case BTF_KIND_UNION: {
+               struct btf_member *member = (struct btf_member *)(t + 1);
+               __u16 vlen = BTF_INFO_VLEN(t->info);
+
+               for (i = 0; i < vlen; i++) {
+                       r = btf_dedup_remap_type_id(d, member->type);
+                       if (r < 0)
+                               return r;
+                       member->type = r;
+                       member++;
+               }
+               break;
+       }
+
+       case BTF_KIND_FUNC_PROTO: {
+               struct btf_param *param = (struct btf_param *)(t + 1);
+               __u16 vlen = BTF_INFO_VLEN(t->info);
+
+               r = btf_dedup_remap_type_id(d, t->type);
+               if (r < 0)
+                       return r;
+               t->type = r;
+
+               for (i = 0; i < vlen; i++) {
+                       r = btf_dedup_remap_type_id(d, param->type);
+                       if (r < 0)
+                               return r;
+                       param->type = r;
+                       param++;
+               }
+               break;
+       }
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int btf_dedup_remap_types(struct btf_dedup *d)
+{
+       int i, r;
+
+       for (i = 1; i <= d->btf->nr_types; i++) {
+               r = btf_dedup_remap_type(d, i);
+               if (r < 0)
+                       return r;
+       }
+       return 0;
+}
index b0610dc..94bbc24 100644 (file)
@@ -55,33 +55,46 @@ struct btf_ext_header {
        __u32   line_info_len;
 };
 
-typedef int (*btf_print_fn_t)(const char *, ...)
-       __attribute__((format(printf, 1, 2)));
-
 LIBBPF_API void btf__free(struct btf *btf);
-LIBBPF_API struct btf *btf__new(__u8 *data, __u32 size, btf_print_fn_t err_log);
+LIBBPF_API struct btf *btf__new(__u8 *data, __u32 size);
+LIBBPF_API int btf__load(struct btf *btf);
 LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
                                   const char *type_name);
+LIBBPF_API __u32 btf__get_nr_types(const struct btf *btf);
 LIBBPF_API const struct btf_type *btf__type_by_id(const struct btf *btf,
                                                  __u32 id);
 LIBBPF_API __s64 btf__resolve_size(const struct btf *btf, __u32 type_id);
 LIBBPF_API int btf__resolve_type(const struct btf *btf, __u32 type_id);
 LIBBPF_API int btf__fd(const struct btf *btf);
+LIBBPF_API const void *btf__get_raw_data(const struct btf *btf, __u32 *size);
 LIBBPF_API const char *btf__name_by_offset(const struct btf *btf, __u32 offset);
 LIBBPF_API int btf__get_from_id(__u32 id, struct btf **btf);
+LIBBPF_API int btf__get_map_kv_tids(const struct btf *btf, const char *map_name,
+                                   __u32 expected_key_size,
+                                   __u32 expected_value_size,
+                                   __u32 *key_type_id, __u32 *value_type_id);
+
+LIBBPF_API struct btf_ext *btf_ext__new(__u8 *data, __u32 size);
+LIBBPF_API void btf_ext__free(struct btf_ext *btf_ext);
+LIBBPF_API const void *btf_ext__get_raw_data(const struct btf_ext* btf_ext,
+                                            __u32 *size);
+LIBBPF_API int btf_ext__reloc_func_info(const struct btf *btf,
+                                       const struct btf_ext *btf_ext,
+                                       const char *sec_name, __u32 insns_cnt,
+                                       void **func_info, __u32 *cnt);
+LIBBPF_API int btf_ext__reloc_line_info(const struct btf *btf,
+                                       const struct btf_ext *btf_ext,
+                                       const char *sec_name, __u32 insns_cnt,
+                                       void **line_info, __u32 *cnt);
+LIBBPF_API __u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext);
+LIBBPF_API __u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext);
+
+struct btf_dedup_opts {
+       bool dont_resolve_fwds;
+};
 
-struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log);
-void btf_ext__free(struct btf_ext *btf_ext);
-int btf_ext__reloc_func_info(const struct btf *btf,
-                            const struct btf_ext *btf_ext,
-                            const char *sec_name, __u32 insns_cnt,
-                            void **func_info, __u32 *func_info_len);
-int btf_ext__reloc_line_info(const struct btf *btf,
-                            const struct btf_ext *btf_ext,
-                            const char *sec_name, __u32 insns_cnt,
-                            void **line_info, __u32 *cnt);
-__u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext);
-__u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext);
+LIBBPF_API int btf__dedup(struct btf *btf, struct btf_ext *btf_ext,
+                         const struct btf_dedup_opts *opts);
 
 #ifdef __cplusplus
 } /* extern "C" */
index 2ccde17..b38dcbe 100644 (file)
@@ -42,6 +42,7 @@
 #include "bpf.h"
 #include "btf.h"
 #include "str_error.h"
+#include "libbpf_util.h"
 
 #ifndef EM_BPF
 #define EM_BPF 247
 
 #define __printf(a, b) __attribute__((format(printf, a, b)))
 
-__printf(1, 2)
-static int __base_pr(const char *format, ...)
+static int __base_pr(enum libbpf_print_level level, const char *format,
+                    va_list args)
 {
-       va_list args;
-       int err;
+       if (level == LIBBPF_DEBUG)
+               return 0;
 
-       va_start(args, format);
-       err = vfprintf(stderr, format, args);
-       va_end(args);
-       return err;
+       return vfprintf(stderr, format, args);
 }
 
-static __printf(1, 2) libbpf_print_fn_t __pr_warning = __base_pr;
-static __printf(1, 2) libbpf_print_fn_t __pr_info = __base_pr;
-static __printf(1, 2) libbpf_print_fn_t __pr_debug;
-
-#define __pr(func, fmt, ...)   \
-do {                           \
-       if ((func))             \
-               (func)("libbpf: " fmt, ##__VA_ARGS__); \
-} while (0)
+static libbpf_print_fn_t __libbpf_pr = __base_pr;
 
-#define pr_warning(fmt, ...)   __pr(__pr_warning, fmt, ##__VA_ARGS__)
-#define pr_info(fmt, ...)      __pr(__pr_info, fmt, ##__VA_ARGS__)
-#define pr_debug(fmt, ...)     __pr(__pr_debug, fmt, ##__VA_ARGS__)
+void libbpf_set_print(libbpf_print_fn_t fn)
+{
+       __libbpf_pr = fn;
+}
 
-void libbpf_set_print(libbpf_print_fn_t warn,
-                     libbpf_print_fn_t info,
-                     libbpf_print_fn_t debug)
+__printf(2, 3)
+void libbpf_print(enum libbpf_print_level level, const char *format, ...)
 {
-       __pr_warning = warn;
-       __pr_info = info;
-       __pr_debug = debug;
+       va_list args;
+
+       if (!__libbpf_pr)
+               return;
+
+       va_start(args, format);
+       __libbpf_pr(level, format, args);
+       va_end(args);
 }
 
 #define STRERR_BUFSIZE  128
@@ -312,7 +307,7 @@ bpf_program__init(void *data, size_t size, char *section_name, int idx,
                return -EINVAL;
        }
 
-       bzero(prog, sizeof(*prog));
+       memset(prog, 0, sizeof(*prog));
 
        prog->section_name = strdup(section_name);
        if (!prog->section_name) {
@@ -839,9 +834,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
                else if (strcmp(name, "maps") == 0)
                        obj->efile.maps_shndx = idx;
                else if (strcmp(name, BTF_ELF_SEC) == 0) {
-                       obj->btf = btf__new(data->d_buf, data->d_size,
-                                           __pr_debug);
-                       if (IS_ERR(obj->btf)) {
+                       obj->btf = btf__new(data->d_buf, data->d_size);
+                       if (IS_ERR(obj->btf) || btf__load(obj->btf)) {
                                pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
                                           BTF_ELF_SEC, PTR_ERR(obj->btf));
                                obj->btf = NULL;
@@ -915,8 +909,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
                                 BTF_EXT_ELF_SEC, BTF_ELF_SEC);
                } else {
                        obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
-                                                   btf_ext_data->d_size,
-                                                   __pr_debug);
+                                                   btf_ext_data->d_size);
                        if (IS_ERR(obj->btf_ext)) {
                                pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
                                           BTF_EXT_ELF_SEC,
@@ -1057,72 +1050,18 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
 
 static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
 {
-       const struct btf_type *container_type;
-       const struct btf_member *key, *value;
        struct bpf_map_def *def = &map->def;
-       const size_t max_name = 256;
-       char container_name[max_name];
-       __s64 key_size, value_size;
-       __s32 container_id;
-
-       if (snprintf(container_name, max_name, "____btf_map_%s", map->name) ==
-           max_name) {
-               pr_warning("map:%s length of '____btf_map_%s' is too long\n",
-                          map->name, map->name);
-               return -EINVAL;
-       }
-
-       container_id = btf__find_by_name(btf, container_name);
-       if (container_id < 0) {
-               pr_debug("map:%s container_name:%s cannot be found in BTF. Missing BPF_ANNOTATE_KV_PAIR?\n",
-                        map->name, container_name);
-               return container_id;
-       }
-
-       container_type = btf__type_by_id(btf, container_id);
-       if (!container_type) {
-               pr_warning("map:%s cannot find BTF type for container_id:%u\n",
-                          map->name, container_id);
-               return -EINVAL;
-       }
-
-       if (BTF_INFO_KIND(container_type->info) != BTF_KIND_STRUCT ||
-           BTF_INFO_VLEN(container_type->info) < 2) {
-               pr_warning("map:%s container_name:%s is an invalid container struct\n",
-                          map->name, container_name);
-               return -EINVAL;
-       }
-
-       key = (struct btf_member *)(container_type + 1);
-       value = key + 1;
-
-       key_size = btf__resolve_size(btf, key->type);
-       if (key_size < 0) {
-               pr_warning("map:%s invalid BTF key_type_size\n",
-                          map->name);
-               return key_size;
-       }
-
-       if (def->key_size != key_size) {
-               pr_warning("map:%s btf_key_type_size:%u != map_def_key_size:%u\n",
-                          map->name, (__u32)key_size, def->key_size);
-               return -EINVAL;
-       }
-
-       value_size = btf__resolve_size(btf, value->type);
-       if (value_size < 0) {
-               pr_warning("map:%s invalid BTF value_type_size\n", map->name);
-               return value_size;
-       }
+       __u32 key_type_id, value_type_id;
+       int ret;
 
-       if (def->value_size != value_size) {
-               pr_warning("map:%s btf_value_type_size:%u != map_def_value_size:%u\n",
-                          map->name, (__u32)value_size, def->value_size);
-               return -EINVAL;
-       }
+       ret = btf__get_map_kv_tids(btf, map->name, def->key_size,
+                                  def->value_size, &key_type_id,
+                                  &value_type_id);
+       if (ret)
+               return ret;
 
-       map->btf_key_type_id = key->type;
-       map->btf_value_type_id = value->type;
+       map->btf_key_type_id = key_type_id;
+       map->btf_value_type_id = value_type_id;
 
        return 0;
 }
@@ -1174,6 +1113,20 @@ err_free_new_name:
        return -errno;
 }
 
+int bpf_map__resize(struct bpf_map *map, __u32 max_entries)
+{
+       if (!map || !max_entries)
+               return -EINVAL;
+
+       /* If map already created, its attributes can't be changed. */
+       if (map->fd >= 0)
+               return -EBUSY;
+
+       map->def.max_entries = max_entries;
+
+       return 0;
+}
+
 static int
 bpf_object__probe_name(struct bpf_object *obj)
 {
@@ -1637,7 +1590,7 @@ bpf_program__load(struct bpf_program *prog,
                struct bpf_prog_prep_result result;
                bpf_program_prep_t preprocessor = prog->preprocessor;
 
-               bzero(&result, sizeof(result));
+               memset(&result, 0, sizeof(result));
                err = preprocessor(prog, i, prog->insns,
                                   prog->insns_cnt, &result);
                if (err) {
@@ -2378,6 +2331,11 @@ unsigned int bpf_object__kversion(struct bpf_object *obj)
        return obj ? obj->kern_version : 0;
 }
 
+struct btf *bpf_object__btf(struct bpf_object *obj)
+{
+       return obj ? obj->btf : NULL;
+}
+
 int bpf_object__btf_fd(const struct bpf_object *obj)
 {
        return obj->btf ? btf__fd(obj->btf) : -1;
@@ -2884,6 +2842,12 @@ bpf_object__find_map_by_name(struct bpf_object *obj, const char *name)
        return NULL;
 }
 
+int
+bpf_object__find_map_fd_by_name(struct bpf_object *obj, const char *name)
+{
+       return bpf_map__fd(bpf_object__find_map_by_name(obj, name));
+}
+
 struct bpf_map *
 bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset)
 {
index 62ae6cb..6c0168f 100644 (file)
@@ -47,17 +47,16 @@ enum libbpf_errno {
 
 LIBBPF_API int libbpf_strerror(int err, char *buf, size_t size);
 
-/*
- * __printf is defined in include/linux/compiler-gcc.h. However,
- * it would be better if libbpf.h didn't depend on Linux header files.
- * So instead of __printf, here we use gcc attribute directly.
- */
-typedef int (*libbpf_print_fn_t)(const char *, ...)
-       __attribute__((format(printf, 1, 2)));
+enum libbpf_print_level {
+        LIBBPF_WARN,
+        LIBBPF_INFO,
+        LIBBPF_DEBUG,
+};
+
+typedef int (*libbpf_print_fn_t)(enum libbpf_print_level level,
+                                const char *, va_list ap);
 
-LIBBPF_API void libbpf_set_print(libbpf_print_fn_t warn,
-                                libbpf_print_fn_t info,
-                                libbpf_print_fn_t debug);
+LIBBPF_API void libbpf_set_print(libbpf_print_fn_t fn);
 
 /* Hide internal to user */
 struct bpf_object;
@@ -90,6 +89,9 @@ LIBBPF_API int bpf_object__load(struct bpf_object *obj);
 LIBBPF_API int bpf_object__unload(struct bpf_object *obj);
 LIBBPF_API const char *bpf_object__name(struct bpf_object *obj);
 LIBBPF_API unsigned int bpf_object__kversion(struct bpf_object *obj);
+
+struct btf;
+LIBBPF_API struct btf *bpf_object__btf(struct bpf_object *obj);
 LIBBPF_API int bpf_object__btf_fd(const struct bpf_object *obj);
 
 LIBBPF_API struct bpf_program *
@@ -264,6 +266,9 @@ struct bpf_map;
 LIBBPF_API struct bpf_map *
 bpf_object__find_map_by_name(struct bpf_object *obj, const char *name);
 
+LIBBPF_API int
+bpf_object__find_map_fd_by_name(struct bpf_object *obj, const char *name);
+
 /*
  * Get bpf_map through the offset of corresponding struct bpf_map_def
  * in the BPF object file.
@@ -292,6 +297,7 @@ LIBBPF_API int bpf_map__set_priv(struct bpf_map *map, void *priv,
                                 bpf_map_clear_priv_t clear_priv);
 LIBBPF_API void *bpf_map__priv(struct bpf_map *map);
 LIBBPF_API int bpf_map__reuse_fd(struct bpf_map *map, int fd);
+LIBBPF_API int bpf_map__resize(struct bpf_map *map, __u32 max_entries);
 LIBBPF_API bool bpf_map__is_offload_neutral(struct bpf_map *map);
 LIBBPF_API void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
 LIBBPF_API int bpf_map__pin(struct bpf_map *map, const char *path);
@@ -314,6 +320,7 @@ LIBBPF_API int bpf_prog_load(const char *file, enum bpf_prog_type type,
                             struct bpf_object **pobj, int *prog_fd);
 
 LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
+LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags);
 
 enum bpf_perf_event_ret {
        LIBBPF_PERF_EVENT_DONE  = 0,
index 266bc95..99dfa71 100644 (file)
@@ -130,4 +130,21 @@ LIBBPF_0.0.2 {
                bpf_probe_helper;
                bpf_probe_map_type;
                bpf_probe_prog_type;
+               bpf_map__resize;
+               bpf_map_lookup_elem_flags;
+               bpf_object__btf;
+               bpf_object__find_map_fd_by_name;
+               bpf_get_link_xdp_id;
+               btf__dedup;
+               btf__get_map_kv_tids;
+               btf__get_nr_types;
+               btf__get_raw_data;
+               btf__load;
+               btf_ext__free;
+               btf_ext__func_info_rec_size;
+               btf_ext__get_raw_data;
+               btf_ext__line_info_rec_size;
+               btf_ext__new;
+               btf_ext__reloc_func_info;
+               btf_ext__reloc_line_info;
 } LIBBPF_0.0.1;
diff --git a/tools/lib/bpf/libbpf_util.h b/tools/lib/bpf/libbpf_util.h
new file mode 100644 (file)
index 0000000..81ecda0
--- /dev/null
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* Copyright (c) 2019 Facebook */
+
+#ifndef __LIBBPF_LIBBPF_UTIL_H
+#define __LIBBPF_LIBBPF_UTIL_H
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void libbpf_print(enum libbpf_print_level level,
+                        const char *format, ...)
+       __attribute__((format(printf, 2, 3)));
+
+#define __pr(level, fmt, ...)  \
+do {                           \
+       libbpf_print(level, "libbpf: " fmt, ##__VA_ARGS__);     \
+} while (0)
+
+#define pr_warning(fmt, ...)   __pr(LIBBPF_WARN, fmt, ##__VA_ARGS__)
+#define pr_info(fmt, ...)      __pr(LIBBPF_INFO, fmt, ##__VA_ARGS__)
+#define pr_debug(fmt, ...)     __pr(LIBBPF_DEBUG, fmt, ##__VA_ARGS__)
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
index 0ce67ae..ce3ec81 100644 (file)
 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
                              void *cookie);
 
+struct xdp_id_md {
+       int ifindex;
+       __u32 flags;
+       __u32 id;
+};
+
 int libbpf_netlink_open(__u32 *nl_pid)
 {
        struct sockaddr_nl sa;
@@ -196,6 +202,85 @@ static int __dump_link_nlmsg(struct nlmsghdr *nlh,
        return dump_link_nlmsg(cookie, ifi, tb);
 }
 
+static unsigned char get_xdp_id_attr(unsigned char mode, __u32 flags)
+{
+       if (mode != XDP_ATTACHED_MULTI)
+               return IFLA_XDP_PROG_ID;
+       if (flags & XDP_FLAGS_DRV_MODE)
+               return IFLA_XDP_DRV_PROG_ID;
+       if (flags & XDP_FLAGS_HW_MODE)
+               return IFLA_XDP_HW_PROG_ID;
+       if (flags & XDP_FLAGS_SKB_MODE)
+               return IFLA_XDP_SKB_PROG_ID;
+
+       return IFLA_XDP_UNSPEC;
+}
+
+static int get_xdp_id(void *cookie, void *msg, struct nlattr **tb)
+{
+       struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
+       struct xdp_id_md *xdp_id = cookie;
+       struct ifinfomsg *ifinfo = msg;
+       unsigned char mode, xdp_attr;
+       int ret;
+
+       if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
+               return 0;
+
+       if (!tb[IFLA_XDP])
+               return 0;
+
+       ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
+       if (ret)
+               return ret;
+
+       if (!xdp_tb[IFLA_XDP_ATTACHED])
+               return 0;
+
+       mode = libbpf_nla_getattr_u8(xdp_tb[IFLA_XDP_ATTACHED]);
+       if (mode == XDP_ATTACHED_NONE)
+               return 0;
+
+       xdp_attr = get_xdp_id_attr(mode, xdp_id->flags);
+       if (!xdp_attr || !xdp_tb[xdp_attr])
+               return 0;
+
+       xdp_id->id = libbpf_nla_getattr_u32(xdp_tb[xdp_attr]);
+
+       return 0;
+}
+
+int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
+{
+       struct xdp_id_md xdp_id = {};
+       int sock, ret;
+       __u32 nl_pid;
+       __u32 mask;
+
+       if (flags & ~XDP_FLAGS_MASK)
+               return -EINVAL;
+
+       /* Check whether the single {HW,DRV,SKB} mode is set */
+       flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE);
+       mask = flags - 1;
+       if (flags && flags & mask)
+               return -EINVAL;
+
+       sock = libbpf_netlink_open(&nl_pid);
+       if (sock < 0)
+               return sock;
+
+       xdp_id.ifindex = ifindex;
+       xdp_id.flags = flags;
+
+       ret = libbpf_nl_get_link(sock, nl_pid, get_xdp_id, &xdp_id);
+       if (!ret)
+               *prog_id = xdp_id.id;
+
+       close(sock);
+       return ret;
+}
+
 int libbpf_nl_get_link(int sock, unsigned int nl_pid,
                       libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
 {
index abf3fc2..fc13487 100644 (file)
@@ -8,11 +8,11 @@
 int main(int argc, char *argv[])
 {
     /* libbpf.h */
-    libbpf_set_print(NULL, NULL, NULL);
+    libbpf_set_print(NULL);
 
     /* bpf.h */
     bpf_prog_get_fd_by_id(0);
 
     /* btf.h */
-    btf__new(NULL, 0, NULL);
+    btf__new(NULL, 0);
 }
index 095aebd..e6150f2 100644 (file)
@@ -19,8 +19,11 @@ C2C stands for Cache To Cache.
 The perf c2c tool provides means for Shared Data C2C/HITM analysis. It allows
 you to track down the cacheline contentions.
 
-The tool is based on x86's load latency and precise store facility events
-provided by Intel CPUs. These events provide:
+On x86, the tool is based on load latency and precise store facility events
+provided by Intel CPUs. On PowerPC, the tool uses random instruction sampling
+with thresholding feature.
+
+These events provide:
   - memory address of the access
   - type of the access (load and store details)
   - latency (in cycles) of the load access
@@ -46,7 +49,7 @@ RECORD OPTIONS
 
 -l::
 --ldlat::
-       Configure mem-loads latency.
+       Configure mem-loads latency. (x86 only)
 
 -k::
 --all-kernel::
@@ -119,11 +122,16 @@ Following perf record options are configured by default:
   -W,-d,--phys-data,--sample-cpu
 
 Unless specified otherwise with '-e' option, following events are monitored by
-default:
+default on x86:
 
   cpu/mem-loads,ldlat=30/P
   cpu/mem-stores/P
 
+and following on PowerPC:
+
+  cpu/mem-loads/
+  cpu/mem-stores/
+
 User can pass any 'perf record' option behind '--' mark, like (to enable
 callchains and system wide monitoring):
 
index f8d2167..199ea0f 100644 (file)
@@ -82,7 +82,7 @@ RECORD OPTIONS
        Be more verbose (show counter open errors, etc)
 
 --ldlat <n>::
-       Specify desired latency for loads event.
+       Specify desired latency for loads event. (x86 only)
 
 In addition, for report all perf report options are valid, and for record
 all perf record options.
index 2e65953..ba98bd0 100644 (file)
@@ -2,6 +2,7 @@ libperf-y += header.o
 libperf-y += sym-handling.o
 libperf-y += kvm-stat.o
 libperf-y += perf_regs.o
+libperf-y += mem-events.o
 
 libperf-$(CONFIG_DWARF) += dwarf-regs.o
 libperf-$(CONFIG_DWARF) += skip-callchain-idx.o
diff --git a/tools/perf/arch/powerpc/util/mem-events.c b/tools/perf/arch/powerpc/util/mem-events.c
new file mode 100644 (file)
index 0000000..d08311f
--- /dev/null
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "mem-events.h"
+
+/* PowerPC does not support 'ldlat' parameter. */
+char *perf_mem_events__name(int i)
+{
+       if (i == PERF_MEM_EVENTS__LOAD)
+               return (char *) "cpu/mem-loads/";
+
+       return (char *) "cpu/mem-stores/";
+}
index d079f36..ac221f1 100644 (file)
@@ -1681,13 +1681,8 @@ static void perf_sample__fprint_metric(struct perf_script *script,
                .force_header = false,
        };
        struct perf_evsel *ev2;
-       static bool init;
        u64 val;
 
-       if (!init) {
-               perf_stat__init_shadow_stats();
-               init = true;
-       }
        if (!evsel->stats)
                perf_evlist__alloc_stats(script->session->evlist, false);
        if (evsel_script(evsel->leader)->gnum++ == 0)
@@ -1794,7 +1789,7 @@ static void process_event(struct perf_script *script,
                return;
        }
 
-       if (PRINT_FIELD(TRACE)) {
+       if (PRINT_FIELD(TRACE) && sample->raw_data) {
                event_format__fprintf(evsel->tp_format, sample->cpu,
                                      sample->raw_data, sample->raw_size, fp);
        }
@@ -2359,6 +2354,8 @@ static int __cmd_script(struct perf_script *script)
 
        signal(SIGINT, sig_handler);
 
+       perf_stat__init_shadow_stats();
+
        /* override event processing functions */
        if (script->show_task_events) {
                script->tool.comm = process_comm_event;
index ed45831..b36061c 100644 (file)
@@ -2514,19 +2514,30 @@ static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp);
 
 static bool perf_evlist__add_vfs_getname(struct perf_evlist *evlist)
 {
-       struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname");
+       bool found = false;
+       struct perf_evsel *evsel, *tmp;
+       struct parse_events_error err = { .idx = 0, };
+       int ret = parse_events(evlist, "probe:vfs_getname*", &err);
 
-       if (IS_ERR(evsel))
+       if (ret)
                return false;
 
-       if (perf_evsel__field(evsel, "pathname") == NULL) {
+       evlist__for_each_entry_safe(evlist, evsel, tmp) {
+               if (!strstarts(perf_evsel__name(evsel), "probe:vfs_getname"))
+                       continue;
+
+               if (perf_evsel__field(evsel, "pathname")) {
+                       evsel->handler = trace__vfs_getname;
+                       found = true;
+                       continue;
+               }
+
+               list_del_init(&evsel->node);
+               evsel->evlist = NULL;
                perf_evsel__delete(evsel);
-               return false;
        }
 
-       evsel->handler = trace__vfs_getname;
-       perf_evlist__add(evlist, evsel);
-       return true;
+       return found;
 }
 
 static struct perf_evsel *perf_evsel__new_pgfault(u64 config)
index 44090a9..e952127 100644 (file)
@@ -1,6 +1,8 @@
 #! /usr/bin/python
 # SPDX-License-Identifier: GPL-2.0
 
+from __future__ import print_function
+
 import os
 import sys
 import glob
@@ -8,7 +10,11 @@ import optparse
 import tempfile
 import logging
 import shutil
-import ConfigParser
+
+try:
+    import configparser
+except ImportError:
+    import ConfigParser as configparser
 
 def data_equal(a, b):
     # Allow multiple values in assignment separated by '|'
@@ -100,20 +106,20 @@ class Event(dict):
     def equal(self, other):
         for t in Event.terms:
             log.debug("      [%s] %s %s" % (t, self[t], other[t]));
-            if not self.has_key(t) or not other.has_key(t):
+            if t not in self or t not in other:
                 return False
             if not data_equal(self[t], other[t]):
                 return False
         return True
 
     def optional(self):
-        if self.has_key('optional') and self['optional'] == '1':
+        if 'optional' in self and self['optional'] == '1':
             return True
         return False
 
     def diff(self, other):
         for t in Event.terms:
-            if not self.has_key(t) or not other.has_key(t):
+            if t not in self or t not in other:
                 continue
             if not data_equal(self[t], other[t]):
                 log.warning("expected %s=%s, got %s" % (t, self[t], other[t]))
@@ -134,7 +140,7 @@ class Event(dict):
 #   - expected values assignments
 class Test(object):
     def __init__(self, path, options):
-        parser = ConfigParser.SafeConfigParser()
+        parser = configparser.SafeConfigParser()
         parser.read(path)
 
         log.warning("running '%s'" % path)
@@ -193,7 +199,7 @@ class Test(object):
         return True
 
     def load_events(self, path, events):
-        parser_event = ConfigParser.SafeConfigParser()
+        parser_event = configparser.SafeConfigParser()
         parser_event.read(path)
 
         # The event record section header contains 'event' word,
@@ -207,7 +213,7 @@ class Test(object):
             # Read parent event if there's any
             if (':' in section):
                 base = section[section.index(':') + 1:]
-                parser_base = ConfigParser.SafeConfigParser()
+                parser_base = configparser.SafeConfigParser()
                 parser_base.read(self.test_dir + '/' + base)
                 base_items = parser_base.items('event')
 
@@ -322,9 +328,9 @@ def run_tests(options):
     for f in glob.glob(options.test_dir + '/' + options.test):
         try:
             Test(f, options).run()
-        except Unsup, obj:
+        except Unsup as obj:
             log.warning("unsupp  %s" % obj.getMsg())
-        except Notest, obj:
+        except Notest as obj:
             log.warning("skipped %s" % obj.getMsg())
 
 def setup_log(verbose):
@@ -363,7 +369,7 @@ def main():
     parser.add_option("-p", "--perf",
                       action="store", type="string", dest="perf")
     parser.add_option("-v", "--verbose",
-                      action="count", dest="verbose")
+                      default=0, action="count", dest="verbose")
 
     options, args = parser.parse_args()
     if args:
@@ -373,7 +379,7 @@ def main():
     setup_log(options.verbose)
 
     if not options.test_dir:
-        print 'FAILED no -d option specified'
+        print('FAILED no -d option specified')
         sys.exit(-1)
 
     if not options.test:
@@ -382,8 +388,8 @@ def main():
     try:
         run_tests(options)
 
-    except Fail, obj:
-        print "FAILED %s" % obj.getMsg();
+    except Fail as obj:
+        print("FAILED %s" % obj.getMsg())
         sys.exit(-1)
 
     sys.exit(0)
index 5f8501c..5cbba70 100644 (file)
@@ -17,7 +17,7 @@ static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name,
                return -1;
        }
 
-       is_signed = !!(field->flags | TEP_FIELD_IS_SIGNED);
+       is_signed = !!(field->flags & TEP_FIELD_IS_SIGNED);
        if (should_be_signed && !is_signed) {
                pr_debug("%s: \"%s\" signedness(%d) is wrong, should be %d\n",
                         evsel->name, name, is_signed, should_be_signed);
index 1d00e5e..82e16bf 100644 (file)
@@ -224,20 +224,24 @@ static unsigned int annotate_browser__refresh(struct ui_browser *browser)
        return ret;
 }
 
-static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
+static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
+                                                 int percent_type)
 {
        int i;
 
        for (i = 0; i < a->data_nr; i++) {
-               if (a->data[i].percent == b->data[i].percent)
+               if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
                        continue;
-               return a->data[i].percent < b->data[i].percent;
+               return a->data[i].percent[percent_type] -
+                          b->data[i].percent[percent_type];
        }
        return 0;
 }
 
-static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
+static void disasm_rb_tree__insert(struct annotate_browser *browser,
+                               struct annotation_line *al)
 {
+       struct rb_root *root = &browser->entries;
        struct rb_node **p = &root->rb_node;
        struct rb_node *parent = NULL;
        struct annotation_line *l;
@@ -246,7 +250,7 @@ static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line
                parent = *p;
                l = rb_entry(parent, struct annotation_line, rb_node);
 
-               if (disasm__cmp(al, l))
+               if (disasm__cmp(al, l, browser->opts->percent_type) < 0)
                        p = &(*p)->rb_left;
                else
                        p = &(*p)->rb_right;
@@ -329,7 +333,7 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
                        RB_CLEAR_NODE(&pos->al.rb_node);
                        continue;
                }
-               disasm_rb_tree__insert(&browser->entries, &pos->al);
+               disasm_rb_tree__insert(browser, &pos->al);
        }
        pthread_mutex_unlock(&notes->lock);
 
index 2f3eb6d..037d8ff 100644 (file)
 #include "llvm-utils.h"
 #include "c++/clang-c.h"
 
-#define DEFINE_PRINT_FN(name, level) \
-static int libbpf_##name(const char *fmt, ...) \
-{                                              \
-       va_list args;                           \
-       int ret;                                \
-                                               \
-       va_start(args, fmt);                    \
-       ret = veprintf(level, verbose, pr_fmt(fmt), args);\
-       va_end(args);                           \
-       return ret;                             \
+static int libbpf_perf_print(enum libbpf_print_level level __attribute__((unused)),
+                             const char *fmt, va_list args)
+{
+       return veprintf(1, verbose, pr_fmt(fmt), args);
 }
 
-DEFINE_PRINT_FN(warning, 1)
-DEFINE_PRINT_FN(info, 1)
-DEFINE_PRINT_FN(debug, 1)
-
 struct bpf_prog_priv {
        bool is_tp;
        char *sys_name;
@@ -59,9 +49,7 @@ bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name)
        struct bpf_object *obj;
 
        if (!libbpf_initialized) {
-               libbpf_set_print(libbpf_warning,
-                                libbpf_info,
-                                libbpf_debug);
+               libbpf_set_print(libbpf_perf_print);
                libbpf_initialized = true;
        }
 
@@ -79,9 +67,7 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
        struct bpf_object *obj;
 
        if (!libbpf_initialized) {
-               libbpf_set_print(libbpf_warning,
-                                libbpf_info,
-                                libbpf_debug);
+               libbpf_set_print(libbpf_perf_print);
                libbpf_initialized = true;
        }
 
index 8951250..39c0004 100644 (file)
@@ -160,7 +160,7 @@ getBPFObjectFromModule(llvm::Module *Module)
        }
        PM.run(*Module);
 
-       return std::move(Buffer);
+       return Buffer;
 }
 
 }
index 1ccbd33..383674f 100644 (file)
@@ -134,7 +134,12 @@ struct cpu_map *cpu_map__new(const char *cpu_list)
        if (!cpu_list)
                return cpu_map__read_all_cpu_map();
 
-       if (!isdigit(*cpu_list))
+       /*
+        * must handle the case of empty cpumap to cover
+        * TOPOLOGY header for NUMA nodes with no CPU
+        * ( e.g., because of CPU hotplug)
+        */
+       if (!isdigit(*cpu_list) && *cpu_list != '\0')
                goto out;
 
        while (isdigit(*cpu_list)) {
@@ -181,8 +186,10 @@ struct cpu_map *cpu_map__new(const char *cpu_list)
 
        if (nr_cpus > 0)
                cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
-       else
+       else if (*cpu_list != '\0')
                cpus = cpu_map__default_new();
+       else
+               cpus = cpu_map__dummy_new();
 invalid:
        free(tmp_cpus);
 out:
index 93f74d8..42c3e5a 100644 (file)
@@ -28,7 +28,7 @@ struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
 static char mem_loads_name[100];
 static bool mem_loads_name__init;
 
-char *perf_mem_events__name(int i)
+char * __weak perf_mem_events__name(int i)
 {
        if (i == PERF_MEM_EVENTS__LOAD) {
                if (!mem_loads_name__init) {
index 8975895..ea523d3 100644 (file)
@@ -391,8 +391,10 @@ void ordered_events__free(struct ordered_events *oe)
         * Current buffer might not have all the events allocated
         * yet, we need to free only allocated ones ...
         */
-       list_del(&oe->buffer->list);
-       ordered_events_buffer__free(oe->buffer, oe->buffer_idx, oe);
+       if (oe->buffer) {
+               list_del(&oe->buffer->list);
+               ordered_events_buffer__free(oe->buffer, oe->buffer_idx, oe);
+       }
 
        /* ... and continue with the rest */
        list_for_each_entry_safe(buffer, tmp, &oe->to_free, list) {
index 63f758c..64d1f36 100644 (file)
@@ -17,6 +17,8 @@ if cc == "clang":
             vars[var] = sub("-mcet", "", vars[var])
         if not clang_has_option("-fcf-protection"):
             vars[var] = sub("-fcf-protection", "", vars[var])
+        if not clang_has_option("-fstack-clash-protection"):
+            vars[var] = sub("-fstack-clash-protection", "", vars[var])
 
 from distutils.core import setup, Extension
 
index 66a84d5..dca7dfa 100644 (file)
 #define EM_AARCH64     183  /* ARM 64 bit */
 #endif
 
+#ifndef ELF32_ST_VISIBILITY
+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
+#endif
+
+/* For ELF64 the definitions are the same.  */
+#ifndef ELF64_ST_VISIBILITY
+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
+#endif
+
+/* How to extract information held in the st_other field.  */
+#ifndef GELF_ST_VISIBILITY
+#define GELF_ST_VISIBILITY(val)        ELF64_ST_VISIBILITY (val)
+#endif
+
 typedef Elf64_Nhdr GElf_Nhdr;
 
 #ifdef HAVE_CPLUS_DEMANGLE_SUPPORT
@@ -87,6 +101,11 @@ static inline uint8_t elf_sym__type(const GElf_Sym *sym)
        return GELF_ST_TYPE(sym->st_info);
 }
 
+static inline uint8_t elf_sym__visibility(const GElf_Sym *sym)
+{
+       return GELF_ST_VISIBILITY(sym->st_other);
+}
+
 #ifndef STT_GNU_IFUNC
 #define STT_GNU_IFUNC 10
 #endif
@@ -111,7 +130,9 @@ static inline int elf_sym__is_label(const GElf_Sym *sym)
        return elf_sym__type(sym) == STT_NOTYPE &&
                sym->st_name != 0 &&
                sym->st_shndx != SHN_UNDEF &&
-               sym->st_shndx != SHN_ABS;
+               sym->st_shndx != SHN_ABS &&
+               elf_sym__visibility(sym) != STV_HIDDEN &&
+               elf_sym__visibility(sym) != STV_INTERNAL;
 }
 
 static bool elf_sym__filter(GElf_Sym *sym)
index 1a2bd15..400ee81 100644 (file)
@@ -10,6 +10,7 @@ TARGETS += drivers/dma-buf
 TARGETS += efivarfs
 TARGETS += exec
 TARGETS += filesystems
+TARGETS += filesystems/binderfs
 TARGETS += firmware
 TARGETS += ftrace
 TARGETS += futex
index dd093bd..e47168d 100644 (file)
@@ -29,3 +29,4 @@ test_netcnt
 test_section_names
 test_tcpnotify_user
 test_libbpf
+alu32
index 8993e9c..ccffaa0 100644 (file)
@@ -23,42 +23,19 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
        test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \
        test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \
        test_socket_cookie test_cgroup_storage test_select_reuseport test_section_names \
-       test_netcnt test_tcpnotify_user
-
-BPF_OBJ_FILES = \
-       test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \
-       sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o \
-       test_tcpnotify_kern.o sample_map_ret0.o test_tcpbpf_kern.o \
-       sockmap_tcp_msg_prog.o connect4_prog.o connect6_prog.o \
-       test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o \
-       test_tunnel_kern.o test_sockhash_kern.o test_lwt_seg6local.o \
-       sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \
-       get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o \
-       test_skb_cgroup_id_kern.o bpf_flow.o netcnt_prog.o test_xdp_vlan.o \
-       xdp_dummy.o test_map_in_map.o
-
-# Objects are built with default compilation flags and with sub-register
-# code-gen enabled.
-BPF_OBJ_FILES_DUAL_COMPILE = \
-       test_pkt_access.o test_pkt_access.o test_xdp.o test_adjust_tail.o \
-       test_l4lb.o test_l4lb_noinline.o test_xdp_noinline.o test_tcp_estats.o \
-       test_obj_id.o test_pkt_md_access.o test_tracepoint.o \
-       test_stacktrace_map.o test_stacktrace_map.o test_stacktrace_build_id.o \
-       test_stacktrace_build_id.o test_get_stack_rawtp.o \
-       test_get_stack_rawtp.o test_tracepoint.o test_sk_lookup_kern.o \
-       test_queue_map.o test_stack_map.o
-
-TEST_GEN_FILES = $(BPF_OBJ_FILES) $(BPF_OBJ_FILES_DUAL_COMPILE)
-
-# Also test sub-register code-gen if LLVM + kernel both has eBPF v3 processor
-# support which is the first version to contain both ALU32 and JMP32
-# instructions.
+       test_netcnt test_tcpnotify_user test_sock_fields
+
+BPF_OBJ_FILES = $(patsubst %.c,%.o, $(notdir $(wildcard progs/*.c)))
+TEST_GEN_FILES = $(BPF_OBJ_FILES)
+
+# Also test sub-register code-gen if LLVM has eBPF v3 processor support which
+# contains both ALU32 and JMP32 instructions.
 SUBREG_CODEGEN := $(shell echo "int cal(int a) { return a > 0; }" | \
                        $(CLANG) -target bpf -O2 -emit-llvm -S -x c - -o - | \
-                       $(LLC) -mattr=+alu32 -mcpu=probe 2>&1 | \
+                       $(LLC) -mattr=+alu32 -mcpu=v3 2>&1 | \
                        grep 'if w')
 ifneq ($(SUBREG_CODEGEN),)
-TEST_GEN_FILES += $(patsubst %.o,alu32/%.o, $(BPF_OBJ_FILES_DUAL_COMPILE))
+TEST_GEN_FILES += $(patsubst %.o,alu32/%.o, $(BPF_OBJ_FILES))
 endif
 
 # Order correspond to 'make run_tests' order
@@ -73,7 +50,8 @@ TEST_PROGS := test_kmod.sh \
        test_lirc_mode2.sh \
        test_skb_cgroup_id.sh \
        test_flow_dissector.sh \
-       test_xdp_vlan.sh
+       test_xdp_vlan.sh \
+       test_lwt_ip_encap.sh
 
 TEST_PROGS_EXTENDED := with_addr.sh \
        with_tunnels.sh \
@@ -111,6 +89,7 @@ $(OUTPUT)/test_progs: trace_helpers.c
 $(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c
 $(OUTPUT)/test_cgroup_storage: cgroup_helpers.c
 $(OUTPUT)/test_netcnt: cgroup_helpers.c
+$(OUTPUT)/test_sock_fields: cgroup_helpers.c
 
 .PHONY: force
 
@@ -188,7 +167,8 @@ $(ALU32_BUILD_DIR)/test_progs_32: test_progs.c $(ALU32_BUILD_DIR) \
        $(CC) $(CFLAGS) -o $(ALU32_BUILD_DIR)/test_progs_32 $< \
                trace_helpers.c $(OUTPUT)/libbpf.a $(LDLIBS)
 
-$(ALU32_BUILD_DIR)/%.o: %.c $(ALU32_BUILD_DIR) $(ALU32_BUILD_DIR)/test_progs_32
+$(ALU32_BUILD_DIR)/%.o: progs/%.c $(ALU32_BUILD_DIR) \
+                                       $(ALU32_BUILD_DIR)/test_progs_32
        $(CLANG) $(CLANG_FLAGS) \
                 -O2 -target bpf -emit-llvm -c $< -o - |      \
        $(LLC) -march=bpf -mattr=+alu32 -mcpu=$(CPU) $(LLC_FLAGS) \
@@ -200,7 +180,7 @@ endif
 
 # Have one program compiled without "-target bpf" to test whether libbpf loads
 # it successfully
-$(OUTPUT)/test_xdp.o: test_xdp.c
+$(OUTPUT)/test_xdp.o: progs/test_xdp.c
        $(CLANG) $(CLANG_FLAGS) \
                -O2 -emit-llvm -c $< -o - | \
        $(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
@@ -208,7 +188,7 @@ ifeq ($(DWARF2BTF),y)
        $(BTF_PAHOLE) -J $@
 endif
 
-$(OUTPUT)/%.o: %.c
+$(OUTPUT)/%.o: progs/%.c
        $(CLANG) $(CLANG_FLAGS) \
                 -O2 -target bpf -emit-llvm -c $< -o - |      \
        $(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
@@ -216,7 +196,8 @@ ifeq ($(DWARF2BTF),y)
        $(BTF_PAHOLE) -J $@
 endif
 
-$(OUTPUT)/test_verifier: $(OUTPUT)/verifier/tests.h
+VERIFIER_TESTS_H := $(OUTPUT)/verifier/tests.h
+$(OUTPUT)/test_verifier: $(VERIFIER_TESTS_H)
 $(OUTPUT)/test_verifier: CFLAGS += -I$(OUTPUT)
 
 VERIFIER_TEST_FILES := $(wildcard verifier/*.c)
@@ -227,6 +208,7 @@ $(OUTPUT)/verifier/tests.h: $(VERIFIER_TEST_FILES)
                  ls *.c 2> /dev/null | \
                        sed -e 's@\(.*\)@#include \"\1\"@'; \
                  echo '#endif' \
-                ) > $(OUTPUT)/verifier/tests.h)
+                ) > $(VERIFIER_TESTS_H))
 
-EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(ALU32_BUILD_DIR)
+EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(ALU32_BUILD_DIR) \
+       $(VERIFIER_TESTS_H)
index 6c77cf7..d9999f1 100644 (file)
@@ -172,6 +172,14 @@ static int (*bpf_skb_vlan_pop)(void *ctx) =
        (void *) BPF_FUNC_skb_vlan_pop;
 static int (*bpf_rc_pointer_rel)(void *ctx, int rel_x, int rel_y) =
        (void *) BPF_FUNC_rc_pointer_rel;
+static void (*bpf_spin_lock)(struct bpf_spin_lock *lock) =
+       (void *) BPF_FUNC_spin_lock;
+static void (*bpf_spin_unlock)(struct bpf_spin_lock *lock) =
+       (void *) BPF_FUNC_spin_unlock;
+static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) =
+       (void *) BPF_FUNC_sk_fullsock;
+static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) =
+       (void *) BPF_FUNC_tcp_sock;
 
 /* llvm builtin functions that eBPF C program may use to
  * emit BPF_LD_ABS and BPF_LD_IND instructions
index 315a44f..a29206e 100644 (file)
@@ -13,7 +13,7 @@ static inline unsigned int bpf_num_possible_cpus(void)
        unsigned int start, end, possible_cpus = 0;
        char buff[128];
        FILE *fp;
-       int n;
+       int len, n, i, j = 0;
 
        fp = fopen(fcpu, "r");
        if (!fp) {
@@ -21,17 +21,27 @@ static inline unsigned int bpf_num_possible_cpus(void)
                exit(1);
        }
 
-       while (fgets(buff, sizeof(buff), fp)) {
-               n = sscanf(buff, "%u-%u", &start, &end);
-               if (n == 0) {
-                       printf("Failed to retrieve # possible CPUs!\n");
-                       exit(1);
-               } else if (n == 1) {
-                       end = start;
+       if (!fgets(buff, sizeof(buff), fp)) {
+               printf("Failed to read %s!\n", fcpu);
+               exit(1);
+       }
+
+       len = strlen(buff);
+       for (i = 0; i <= len; i++) {
+               if (buff[i] == ',' || buff[i] == '\0') {
+                       buff[i] = '\0';
+                       n = sscanf(&buff[j], "%u-%u", &start, &end);
+                       if (n <= 0) {
+                               printf("Failed to retrieve # possible CPUs!\n");
+                               exit(1);
+                       } else if (n == 1) {
+                               end = start;
+                       }
+                       possible_cpus += end - start + 1;
+                       j = i + 1;
                }
-               possible_cpus = start == 0 ? end + 1 : 0;
-               break;
        }
+
        fclose(fp);
 
        return possible_cpus;
@@ -48,4 +58,13 @@ static inline unsigned int bpf_num_possible_cpus(void)
 # define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 #endif
 
+#ifndef sizeof_field
+#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
+#endif
+
+#ifndef offsetofend
+#define offsetofend(TYPE, MEMBER) \
+       (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER))
+#endif
+
 #endif /* __BPF_UTIL__ */
diff --git a/tools/testing/selftests/bpf/progs/test_lwt_ip_encap.c b/tools/testing/selftests/bpf/progs/test_lwt_ip_encap.c
new file mode 100644 (file)
index 0000000..c957d6d
--- /dev/null
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stddef.h>
+#include <string.h>
+#include <linux/bpf.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include "bpf_helpers.h"
+#include "bpf_endian.h"
+
+struct grehdr {
+       __be16 flags;
+       __be16 protocol;
+};
+
+SEC("encap_gre")
+int bpf_lwt_encap_gre(struct __sk_buff *skb)
+{
+       struct encap_hdr {
+               struct iphdr iph;
+               struct grehdr greh;
+       } hdr;
+       int err;
+
+       memset(&hdr, 0, sizeof(struct encap_hdr));
+
+       hdr.iph.ihl = 5;
+       hdr.iph.version = 4;
+       hdr.iph.ttl = 0x40;
+       hdr.iph.protocol = 47;  /* IPPROTO_GRE */
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+       hdr.iph.saddr = 0x640110ac;  /* 172.16.1.100 */
+       hdr.iph.daddr = 0x641010ac;  /* 172.16.16.100 */
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+       hdr.iph.saddr = 0xac100164;  /* 172.16.1.100 */
+       hdr.iph.daddr = 0xac101064;  /* 172.16.16.100 */
+#else
+#error "Fix your compiler's __BYTE_ORDER__?!"
+#endif
+       hdr.iph.tot_len = bpf_htons(skb->len + sizeof(struct encap_hdr));
+
+       hdr.greh.protocol = skb->protocol;
+
+       err = bpf_lwt_push_encap(skb, BPF_LWT_ENCAP_IP, &hdr,
+                                sizeof(struct encap_hdr));
+       if (err)
+               return BPF_DROP;
+
+       return BPF_LWT_REROUTE;
+}
+
+SEC("encap_gre6")
+int bpf_lwt_encap_gre6(struct __sk_buff *skb)
+{
+       struct encap_hdr {
+               struct ipv6hdr ip6hdr;
+               struct grehdr greh;
+       } hdr;
+       int err;
+
+       memset(&hdr, 0, sizeof(struct encap_hdr));
+
+       hdr.ip6hdr.version = 6;
+       hdr.ip6hdr.payload_len = bpf_htons(skb->len + sizeof(struct grehdr));
+       hdr.ip6hdr.nexthdr = 47;  /* IPPROTO_GRE */
+       hdr.ip6hdr.hop_limit = 0x40;
+       /* fb01::1 */
+       hdr.ip6hdr.saddr.s6_addr[0] = 0xfb;
+       hdr.ip6hdr.saddr.s6_addr[1] = 1;
+       hdr.ip6hdr.saddr.s6_addr[15] = 1;
+       /* fb10::1 */
+       hdr.ip6hdr.daddr.s6_addr[0] = 0xfb;
+       hdr.ip6hdr.daddr.s6_addr[1] = 0x10;
+       hdr.ip6hdr.daddr.s6_addr[15] = 1;
+
+       hdr.greh.protocol = skb->protocol;
+
+       err = bpf_lwt_push_encap(skb, BPF_LWT_ENCAP_IP, &hdr,
+                                sizeof(struct encap_hdr));
+       if (err)
+               return BPF_DROP;
+
+       return BPF_LWT_REROUTE;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_map_lock.c b/tools/testing/selftests/bpf/progs/test_map_lock.c
new file mode 100644 (file)
index 0000000..af8cc68
--- /dev/null
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#include <linux/bpf.h>
+#include <linux/version.h>
+#include "bpf_helpers.h"
+
+#define VAR_NUM 16
+
+struct hmap_elem {
+       struct bpf_spin_lock lock;
+       int var[VAR_NUM];
+};
+
+struct bpf_map_def SEC("maps") hash_map = {
+       .type = BPF_MAP_TYPE_HASH,
+       .key_size = sizeof(int),
+       .value_size = sizeof(struct hmap_elem),
+       .max_entries = 1,
+};
+
+BPF_ANNOTATE_KV_PAIR(hash_map, int, struct hmap_elem);
+
+struct array_elem {
+       struct bpf_spin_lock lock;
+       int var[VAR_NUM];
+};
+
+struct bpf_map_def SEC("maps") array_map = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(struct array_elem),
+       .max_entries = 1,
+};
+
+BPF_ANNOTATE_KV_PAIR(array_map, int, struct array_elem);
+
+SEC("map_lock_demo")
+int bpf_map_lock_test(struct __sk_buff *skb)
+{
+       struct hmap_elem zero = {}, *val;
+       int rnd = bpf_get_prandom_u32();
+       int key = 0, err = 1, i;
+       struct array_elem *q;
+
+       val = bpf_map_lookup_elem(&hash_map, &key);
+       if (!val)
+               goto err;
+       /* spin_lock in hash map */
+       bpf_spin_lock(&val->lock);
+       for (i = 0; i < VAR_NUM; i++)
+               val->var[i] = rnd;
+       bpf_spin_unlock(&val->lock);
+
+       /* spin_lock in array */
+       q = bpf_map_lookup_elem(&array_map, &key);
+       if (!q)
+               goto err;
+       bpf_spin_lock(&q->lock);
+       for (i = 0; i < VAR_NUM; i++)
+               q->var[i] = rnd;
+       bpf_spin_unlock(&q->lock);
+       err = 0;
+err:
+       return err;
+}
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c b/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c
new file mode 100644 (file)
index 0000000..de1a43e
--- /dev/null
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2019 Facebook */
+
+#include <linux/bpf.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+
+#include "bpf_helpers.h"
+#include "bpf_endian.h"
+
+enum bpf_array_idx {
+       SRV_IDX,
+       CLI_IDX,
+       __NR_BPF_ARRAY_IDX,
+};
+
+struct bpf_map_def SEC("maps") addr_map = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(__u32),
+       .value_size = sizeof(struct sockaddr_in6),
+       .max_entries = __NR_BPF_ARRAY_IDX,
+};
+
+struct bpf_map_def SEC("maps") sock_result_map = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(__u32),
+       .value_size = sizeof(struct bpf_sock),
+       .max_entries = __NR_BPF_ARRAY_IDX,
+};
+
+struct bpf_map_def SEC("maps") tcp_sock_result_map = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(__u32),
+       .value_size = sizeof(struct bpf_tcp_sock),
+       .max_entries = __NR_BPF_ARRAY_IDX,
+};
+
+struct bpf_map_def SEC("maps") linum_map = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(__u32),
+       .value_size = sizeof(__u32),
+       .max_entries = 1,
+};
+
+static bool is_loopback6(__u32 *a6)
+{
+       return !a6[0] && !a6[1] && !a6[2] && a6[3] == bpf_htonl(1);
+}
+
+static void skcpy(struct bpf_sock *dst,
+                 const struct bpf_sock *src)
+{
+       dst->bound_dev_if = src->bound_dev_if;
+       dst->family = src->family;
+       dst->type = src->type;
+       dst->protocol = src->protocol;
+       dst->mark = src->mark;
+       dst->priority = src->priority;
+       dst->src_ip4 = src->src_ip4;
+       dst->src_ip6[0] = src->src_ip6[0];
+       dst->src_ip6[1] = src->src_ip6[1];
+       dst->src_ip6[2] = src->src_ip6[2];
+       dst->src_ip6[3] = src->src_ip6[3];
+       dst->src_port = src->src_port;
+       dst->dst_ip4 = src->dst_ip4;
+       dst->dst_ip6[0] = src->dst_ip6[0];
+       dst->dst_ip6[1] = src->dst_ip6[1];
+       dst->dst_ip6[2] = src->dst_ip6[2];
+       dst->dst_ip6[3] = src->dst_ip6[3];
+       dst->dst_port = src->dst_port;
+       dst->state = src->state;
+}
+
+static void tpcpy(struct bpf_tcp_sock *dst,
+                 const struct bpf_tcp_sock *src)
+{
+       dst->snd_cwnd = src->snd_cwnd;
+       dst->srtt_us = src->srtt_us;
+       dst->rtt_min = src->rtt_min;
+       dst->snd_ssthresh = src->snd_ssthresh;
+       dst->rcv_nxt = src->rcv_nxt;
+       dst->snd_nxt = src->snd_nxt;
+       dst->snd_una = src->snd_una;
+       dst->mss_cache = src->mss_cache;
+       dst->ecn_flags = src->ecn_flags;
+       dst->rate_delivered = src->rate_delivered;
+       dst->rate_interval_us = src->rate_interval_us;
+       dst->packets_out = src->packets_out;
+       dst->retrans_out = src->retrans_out;
+       dst->total_retrans = src->total_retrans;
+       dst->segs_in = src->segs_in;
+       dst->data_segs_in = src->data_segs_in;
+       dst->segs_out = src->segs_out;
+       dst->data_segs_out = src->data_segs_out;
+       dst->lost_out = src->lost_out;
+       dst->sacked_out = src->sacked_out;
+       dst->bytes_received = src->bytes_received;
+       dst->bytes_acked = src->bytes_acked;
+}
+
+#define RETURN {                                               \
+       linum = __LINE__;                                       \
+       bpf_map_update_elem(&linum_map, &idx0, &linum, 0);      \
+       return 1;                                               \
+}
+
+SEC("cgroup_skb/egress")
+int read_sock_fields(struct __sk_buff *skb)
+{
+       __u32 srv_idx = SRV_IDX, cli_idx = CLI_IDX, idx;
+       struct sockaddr_in6 *srv_sa6, *cli_sa6;
+       struct bpf_tcp_sock *tp, *tp_ret;
+       struct bpf_sock *sk, *sk_ret;
+       __u32 linum, idx0 = 0;
+
+       sk = skb->sk;
+       if (!sk || sk->state == 10)
+               RETURN;
+
+       sk = bpf_sk_fullsock(sk);
+       if (!sk || sk->family != AF_INET6 || sk->protocol != IPPROTO_TCP ||
+           !is_loopback6(sk->src_ip6))
+               RETURN;
+
+       tp = bpf_tcp_sock(sk);
+       if (!tp)
+               RETURN;
+
+       srv_sa6 = bpf_map_lookup_elem(&addr_map, &srv_idx);
+       cli_sa6 = bpf_map_lookup_elem(&addr_map, &cli_idx);
+       if (!srv_sa6 || !cli_sa6)
+               RETURN;
+
+       if (sk->src_port == bpf_ntohs(srv_sa6->sin6_port))
+               idx = srv_idx;
+       else if (sk->src_port == bpf_ntohs(cli_sa6->sin6_port))
+               idx = cli_idx;
+       else
+               RETURN;
+
+       sk_ret = bpf_map_lookup_elem(&sock_result_map, &idx);
+       tp_ret = bpf_map_lookup_elem(&tcp_sock_result_map, &idx);
+       if (!sk_ret || !tp_ret)
+               RETURN;
+
+       skcpy(sk_ret, sk);
+       tpcpy(tp_ret, tp);
+
+       RETURN;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_spin_lock.c b/tools/testing/selftests/bpf/progs/test_spin_lock.c
new file mode 100644 (file)
index 0000000..40f9043
--- /dev/null
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#include <linux/bpf.h>
+#include <linux/version.h>
+#include "bpf_helpers.h"
+
+struct hmap_elem {
+       volatile int cnt;
+       struct bpf_spin_lock lock;
+       int test_padding;
+};
+
+struct bpf_map_def SEC("maps") hmap = {
+       .type = BPF_MAP_TYPE_HASH,
+       .key_size = sizeof(int),
+       .value_size = sizeof(struct hmap_elem),
+       .max_entries = 1,
+};
+
+BPF_ANNOTATE_KV_PAIR(hmap, int, struct hmap_elem);
+
+
+struct cls_elem {
+       struct bpf_spin_lock lock;
+       volatile int cnt;
+};
+
+struct bpf_map_def SEC("maps") cls_map = {
+       .type = BPF_MAP_TYPE_CGROUP_STORAGE,
+       .key_size = sizeof(struct bpf_cgroup_storage_key),
+       .value_size = sizeof(struct cls_elem),
+};
+
+BPF_ANNOTATE_KV_PAIR(cls_map, struct bpf_cgroup_storage_key,
+                    struct cls_elem);
+
+struct bpf_vqueue {
+       struct bpf_spin_lock lock;
+       /* 4 byte hole */
+       unsigned long long lasttime;
+       int credit;
+       unsigned int rate;
+};
+
+struct bpf_map_def SEC("maps") vqueue = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(struct bpf_vqueue),
+       .max_entries = 1,
+};
+
+BPF_ANNOTATE_KV_PAIR(vqueue, int, struct bpf_vqueue);
+#define CREDIT_PER_NS(delta, rate) (((delta) * rate) >> 20)
+
+SEC("spin_lock_demo")
+int bpf_sping_lock_test(struct __sk_buff *skb)
+{
+       volatile int credit = 0, max_credit = 100, pkt_len = 64;
+       struct hmap_elem zero = {}, *val;
+       unsigned long long curtime;
+       struct bpf_vqueue *q;
+       struct cls_elem *cls;
+       int key = 0;
+       int err = 0;
+
+       val = bpf_map_lookup_elem(&hmap, &key);
+       if (!val) {
+               bpf_map_update_elem(&hmap, &key, &zero, 0);
+               val = bpf_map_lookup_elem(&hmap, &key);
+               if (!val) {
+                       err = 1;
+                       goto err;
+               }
+       }
+       /* spin_lock in hash map run time test */
+       bpf_spin_lock(&val->lock);
+       if (val->cnt)
+               val->cnt--;
+       else
+               val->cnt++;
+       if (val->cnt != 0 && val->cnt != 1)
+               err = 1;
+       bpf_spin_unlock(&val->lock);
+
+       /* spin_lock in array. virtual queue demo */
+       q = bpf_map_lookup_elem(&vqueue, &key);
+       if (!q)
+               goto err;
+       curtime = bpf_ktime_get_ns();
+       bpf_spin_lock(&q->lock);
+       q->credit += CREDIT_PER_NS(curtime - q->lasttime, q->rate);
+       q->lasttime = curtime;
+       if (q->credit > max_credit)
+               q->credit = max_credit;
+       q->credit -= pkt_len;
+       credit = q->credit;
+       bpf_spin_unlock(&q->lock);
+
+       /* spin_lock in cgroup local storage */
+       cls = bpf_get_local_storage(&cls_map, 0);
+       bpf_spin_lock(&cls->lock);
+       cls->cnt++;
+       bpf_spin_unlock(&cls->lock);
+
+err:
+       return err;
+}
+char _license[] SEC("license") = "GPL";
index 7f8200a..a53ed58 100755 (executable)
@@ -30,12 +30,11 @@ def send(sock, s):
 
 
 serverPort = int(sys.argv[1])
-HostName = socket.gethostname()
 
 # create active socket
 sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
 try:
-    sock.connect((HostName, serverPort))
+    sock.connect(('localhost', serverPort))
 except socket.error as e:
     sys.exit(1)
 
index b39903f..0ca60d1 100755 (executable)
@@ -35,13 +35,10 @@ MAX_PORTS = 2
 serverPort = SERVER_PORT
 serverSocket = None
 
-HostName = socket.gethostname()
-
 # create passive socket
 serverSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
-host = socket.gethostname()
 
-try: serverSocket.bind((host, 0))
+try: serverSocket.bind(('localhost', 0))
 except socket.error as msg:
     print('bind fails: ' + str(msg))
 
index 179f1d8..02d3143 100644 (file)
@@ -52,18 +52,10 @@ static int count_result(int err)
        return err;
 }
 
-#define __printf(a, b) __attribute__((format(printf, a, b)))
-
-__printf(1, 2)
-static int __base_pr(const char *format, ...)
+static int __base_pr(enum libbpf_print_level level __attribute__((unused)),
+                    const char *format, va_list args)
 {
-       va_list args;
-       int err;
-
-       va_start(args, format);
-       err = vfprintf(stderr, format, args);
-       va_end(args);
-       return err;
+       return vfprintf(stderr, format, args);
 }
 
 #define BTF_INFO_ENC(kind, kind_flag, vlen)                    \
@@ -78,12 +70,21 @@ static int __base_pr(const char *format, ...)
        BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz),       \
        BTF_INT_ENC(encoding, bits_offset, bits)
 
+#define BTF_FWD_ENC(name, kind_flag) \
+       BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_FWD, kind_flag, 0), 0)
+
 #define BTF_ARRAY_ENC(type, index_type, nr_elems)      \
        (type), (index_type), (nr_elems)
 #define BTF_TYPE_ARRAY_ENC(type, index_type, nr_elems) \
        BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ARRAY, 0, 0), 0), \
        BTF_ARRAY_ENC(type, index_type, nr_elems)
 
+#define BTF_STRUCT_ENC(name, nr_elems, sz)     \
+       BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, nr_elems), sz)
+
+#define BTF_UNION_ENC(name, nr_elems, sz)      \
+       BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_UNION, 0, nr_elems), sz)
+
 #define BTF_MEMBER_ENC(name, type, bits_offset)        \
        (name), (type), (bits_offset)
 #define BTF_ENUM_ENC(name, val) (name), (val)
@@ -99,6 +100,12 @@ static int __base_pr(const char *format, ...)
 #define BTF_CONST_ENC(type) \
        BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 0, 0), type)
 
+#define BTF_VOLATILE_ENC(type) \
+       BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_VOLATILE, 0, 0), type)
+
+#define BTF_RESTRICT_ENC(type) \
+       BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_RESTRICT, 0, 0), type)
+
 #define BTF_FUNC_PROTO_ENC(ret_type, nargs) \
        BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, nargs), ret_type)
 
@@ -111,6 +118,10 @@ static int __base_pr(const char *format, ...)
 #define BTF_END_RAW 0xdeadbeef
 #define NAME_TBD 0xdeadb33f
 
+#define NAME_NTH(N) (0xffff0000 | N)
+#define IS_NAME_NTH(X) ((X & 0xffff0000) == 0xffff0000)
+#define GET_NAME_NTH_IDX(X) (X & 0x0000ffff)
+
 #define MAX_NR_RAW_U32 1024
 #define BTF_LOG_BUF_SIZE 65535
 
@@ -119,12 +130,14 @@ static struct args {
        unsigned int file_test_num;
        unsigned int get_info_test_num;
        unsigned int info_raw_test_num;
+       unsigned int dedup_test_num;
        bool raw_test;
        bool file_test;
        bool get_info_test;
        bool pprint_test;
        bool always_log;
        bool info_raw_test;
+       bool dedup_test;
 } args;
 
 static char btf_log_buf[BTF_LOG_BUF_SIZE];
@@ -1889,13 +1902,12 @@ static struct btf_raw_test raw_tests[] = {
 },
 
 {
-       .descr = "func proto (CONST=>TYPEDEF=>FUNC_PROTO)",
+       .descr = "func proto (TYPEDEF=>FUNC_PROTO)",
        .raw_types = {
                BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
                BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
-               BTF_CONST_ENC(4),                               /* [3] */
-               BTF_TYPEDEF_ENC(NAME_TBD, 5),                   /* [4] */
-               BTF_FUNC_PROTO_ENC(0, 2),                       /* [5] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 4),                   /* [3] */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [4] */
                        BTF_FUNC_PROTO_ARG_ENC(0, 1),
                        BTF_FUNC_PROTO_ARG_ENC(0, 2),
                BTF_END_RAW,
@@ -1909,8 +1921,6 @@ static struct btf_raw_test raw_tests[] = {
        .key_type_id = 1,
        .value_type_id = 1,
        .max_entries = 4,
-       .btf_load_err = true,
-       .err_str = "Invalid type_id",
 },
 
 {
@@ -1965,7 +1975,7 @@ static struct btf_raw_test raw_tests[] = {
                /* void (*)(int a, unsigned int <bad_name_off>) */
                BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
                        BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
-                       BTF_FUNC_PROTO_ARG_ENC(0xffffffff, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(0x0fffffff, 2),
                BTF_END_RAW,
        },
        .str_sec = "\0a",
@@ -2835,11 +2845,13 @@ static void *btf_raw_create(const struct btf_header *hdr,
                            const char **ret_next_str)
 {
        const char *next_str = str, *end_str = str + str_sec_size;
+       const char **strs_idx = NULL, **tmp_strs_idx;
+       int strs_cap = 0, strs_cnt = 0, next_str_idx = 0;
        unsigned int size_needed, offset;
        struct btf_header *ret_hdr;
-       int i, type_sec_size;
+       int i, type_sec_size, err = 0;
        uint32_t *ret_types;
-       void *raw_btf;
+       void *raw_btf = NULL;
 
        type_sec_size = get_raw_sec_size(raw_types);
        if (CHECK(type_sec_size < 0, "Cannot get nr_raw_types"))
@@ -2854,17 +2866,44 @@ static void *btf_raw_create(const struct btf_header *hdr,
        memcpy(raw_btf, hdr, sizeof(*hdr));
        offset = sizeof(*hdr);
 
+       /* Index strings */
+       while ((next_str = get_next_str(next_str, end_str))) {
+               if (strs_cnt == strs_cap) {
+                       strs_cap += max(16, strs_cap / 2);
+                       tmp_strs_idx = realloc(strs_idx,
+                                              sizeof(*strs_idx) * strs_cap);
+                       if (CHECK(!tmp_strs_idx,
+                                 "Cannot allocate memory for strs_idx")) {
+                               err = -1;
+                               goto done;
+                       }
+                       strs_idx = tmp_strs_idx;
+               }
+               strs_idx[strs_cnt++] = next_str;
+               next_str += strlen(next_str);
+       }
+
        /* Copy type section */
        ret_types = raw_btf + offset;
        for (i = 0; i < type_sec_size / sizeof(raw_types[0]); i++) {
                if (raw_types[i] == NAME_TBD) {
-                       next_str = get_next_str(next_str, end_str);
-                       if (CHECK(!next_str, "Error in getting next_str")) {
-                               free(raw_btf);
-                               return NULL;
+                       if (CHECK(next_str_idx == strs_cnt,
+                                 "Error in getting next_str #%d",
+                                 next_str_idx)) {
+                               err = -1;
+                               goto done;
                        }
-                       ret_types[i] = next_str - str;
-                       next_str += strlen(next_str);
+                       ret_types[i] = strs_idx[next_str_idx++] - str;
+               } else if (IS_NAME_NTH(raw_types[i])) {
+                       int idx = GET_NAME_NTH_IDX(raw_types[i]);
+
+                       if (CHECK(idx <= 0 || idx > strs_cnt,
+                                 "Error getting string #%d, strs_cnt:%d",
+                                 idx, strs_cnt)) {
+                               err = -1;
+                               goto done;
+                       }
+                       ret_types[i] = strs_idx[idx-1] - str;
                } else {
                        ret_types[i] = raw_types[i];
                }
@@ -2881,8 +2920,17 @@ static void *btf_raw_create(const struct btf_header *hdr,
 
        *btf_size = size_needed;
        if (ret_next_str)
-               *ret_next_str = next_str;
+               *ret_next_str =
+                       next_str_idx < strs_cnt ? strs_idx[next_str_idx] : NULL;
 
+done:
+       if (err) {
+               if (raw_btf)
+                       free(raw_btf);
+               if (strs_idx)
+                       free(strs_idx);
+               return NULL;
+       }
        return raw_btf;
 }
 
@@ -5551,20 +5599,463 @@ static int test_info_raw(void)
        return err;
 }
 
+struct btf_raw_data {
+       __u32 raw_types[MAX_NR_RAW_U32];
+       const char *str_sec;
+       __u32 str_sec_size;
+};
+
+struct btf_dedup_test {
+       const char *descr;
+       struct btf_raw_data input;
+       struct btf_raw_data expect;
+       struct btf_dedup_opts opts;
+};
+
+const struct btf_dedup_test dedup_tests[] = {
+
+{
+       .descr = "dedup: unused strings filtering",
+       .input = {
+               .raw_types = {
+                       BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 32, 4),
+                       BTF_TYPE_INT_ENC(NAME_NTH(5), BTF_INT_SIGNED, 0, 64, 8),
+                       BTF_END_RAW,
+               },
+               BTF_STR_SEC("\0unused\0int\0foo\0bar\0long"),
+       },
+       .expect = {
+               .raw_types = {
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
+                       BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 64, 8),
+                       BTF_END_RAW,
+               },
+               BTF_STR_SEC("\0int\0long"),
+       },
+       .opts = {
+               .dont_resolve_fwds = false,
+       },
+},
+{
+       .descr = "dedup: strings deduplication",
+       .input = {
+               .raw_types = {
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
+                       BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 64, 8),
+                       BTF_TYPE_INT_ENC(NAME_NTH(3), BTF_INT_SIGNED, 0, 32, 4),
+                       BTF_TYPE_INT_ENC(NAME_NTH(4), BTF_INT_SIGNED, 0, 64, 8),
+                       BTF_TYPE_INT_ENC(NAME_NTH(5), BTF_INT_SIGNED, 0, 32, 4),
+                       BTF_END_RAW,
+               },
+               BTF_STR_SEC("\0int\0long int\0int\0long int\0int"),
+       },
+       .expect = {
+               .raw_types = {
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
+                       BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 64, 8),
+                       BTF_END_RAW,
+               },
+               BTF_STR_SEC("\0int\0long int"),
+       },
+       .opts = {
+               .dont_resolve_fwds = false,
+       },
+},
+{
+       .descr = "dedup: struct example #1",
+       /*
+        * struct s {
+        *      struct s *next;
+        *      const int *a;
+        *      int b[16];
+        *      int c;
+        * }
+        */
+       .input = {
+               .raw_types = {
+                       /* int */
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),        /* [1] */
+                       /* int[16] */
+                       BTF_TYPE_ARRAY_ENC(1, 1, 16),                                   /* [2] */
+                       /* struct s { */
+                       BTF_STRUCT_ENC(NAME_NTH(2), 4, 84),                             /* [3] */
+                               BTF_MEMBER_ENC(NAME_NTH(3), 4, 0),      /* struct s *next;      */
+                               BTF_MEMBER_ENC(NAME_NTH(4), 5, 64),     /* const int *a;        */
+                               BTF_MEMBER_ENC(NAME_NTH(5), 2, 128),    /* int b[16];           */
+                               BTF_MEMBER_ENC(NAME_NTH(6), 1, 640),    /* int c;               */
+                       /* ptr -> [3] struct s */
+                       BTF_PTR_ENC(3),                                                 /* [4] */
+                       /* ptr -> [6] const int */
+                       BTF_PTR_ENC(6),                                                 /* [5] */
+                       /* const -> [1] int */
+                       BTF_CONST_ENC(1),                                               /* [6] */
+
+                       /* full copy of the above */
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),        /* [7] */
+                       BTF_TYPE_ARRAY_ENC(7, 7, 16),                                   /* [8] */
+                       BTF_STRUCT_ENC(NAME_NTH(2), 4, 84),                             /* [9] */
+                               BTF_MEMBER_ENC(NAME_NTH(3), 10, 0),
+                               BTF_MEMBER_ENC(NAME_NTH(4), 11, 64),
+                               BTF_MEMBER_ENC(NAME_NTH(5), 8, 128),
+                               BTF_MEMBER_ENC(NAME_NTH(6), 7, 640),
+                       BTF_PTR_ENC(9),                                                 /* [10] */
+                       BTF_PTR_ENC(12),                                                /* [11] */
+                       BTF_CONST_ENC(7),                                               /* [12] */
+                       BTF_END_RAW,
+               },
+               BTF_STR_SEC("\0int\0s\0next\0a\0b\0c\0"),
+       },
+       .expect = {
+               .raw_types = {
+                       /* int */
+                       BTF_TYPE_INT_ENC(NAME_NTH(4), BTF_INT_SIGNED, 0, 32, 4),        /* [1] */
+                       /* int[16] */
+                       BTF_TYPE_ARRAY_ENC(1, 1, 16),                                   /* [2] */
+                       /* struct s { */
+                       BTF_STRUCT_ENC(NAME_NTH(6), 4, 84),                             /* [3] */
+                               BTF_MEMBER_ENC(NAME_NTH(5), 4, 0),      /* struct s *next;      */
+                               BTF_MEMBER_ENC(NAME_NTH(1), 5, 64),     /* const int *a;        */
+                               BTF_MEMBER_ENC(NAME_NTH(2), 2, 128),    /* int b[16];           */
+                               BTF_MEMBER_ENC(NAME_NTH(3), 1, 640),    /* int c;               */
+                       /* ptr -> [3] struct s */
+                       BTF_PTR_ENC(3),                                                 /* [4] */
+                       /* ptr -> [6] const int */
+                       BTF_PTR_ENC(6),                                                 /* [5] */
+                       /* const -> [1] int */
+                       BTF_CONST_ENC(1),                                               /* [6] */
+                       BTF_END_RAW,
+               },
+               BTF_STR_SEC("\0a\0b\0c\0int\0next\0s"),
+       },
+       .opts = {
+               .dont_resolve_fwds = false,
+       },
+},
+{
+       .descr = "dedup: all possible kinds (no duplicates)",
+       .input = {
+               .raw_types = {
+                       BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 8),           /* [1] int */
+                       BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 2), 4),   /* [2] enum */
+                               BTF_ENUM_ENC(NAME_TBD, 0),
+                               BTF_ENUM_ENC(NAME_TBD, 1),
+                       BTF_FWD_ENC(NAME_TBD, 1 /* union kind_flag */),                 /* [3] fwd */
+                       BTF_TYPE_ARRAY_ENC(2, 1, 7),                                    /* [4] array */
+                       BTF_STRUCT_ENC(NAME_TBD, 1, 4),                                 /* [5] struct */
+                               BTF_MEMBER_ENC(NAME_TBD, 1, 0),
+                       BTF_UNION_ENC(NAME_TBD, 1, 4),                                  /* [6] union */
+                               BTF_MEMBER_ENC(NAME_TBD, 1, 0),
+                       BTF_TYPEDEF_ENC(NAME_TBD, 1),                                   /* [7] typedef */
+                       BTF_PTR_ENC(0),                                                 /* [8] ptr */
+                       BTF_CONST_ENC(8),                                               /* [9] const */
+                       BTF_VOLATILE_ENC(8),                                            /* [10] volatile */
+                       BTF_RESTRICT_ENC(8),                                            /* [11] restrict */
+                       BTF_FUNC_PROTO_ENC(1, 2),                                       /* [12] func_proto */
+                               BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                               BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 8),
+                       BTF_FUNC_ENC(NAME_TBD, 12),                                     /* [13] func */
+                       BTF_END_RAW,
+               },
+               BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M"),
+       },
+       .expect = {
+               .raw_types = {
+                       BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 8),           /* [1] int */
+                       BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 2), 4),   /* [2] enum */
+                               BTF_ENUM_ENC(NAME_TBD, 0),
+                               BTF_ENUM_ENC(NAME_TBD, 1),
+                       BTF_FWD_ENC(NAME_TBD, 1 /* union kind_flag */),                 /* [3] fwd */
+                       BTF_TYPE_ARRAY_ENC(2, 1, 7),                                    /* [4] array */
+                       BTF_STRUCT_ENC(NAME_TBD, 1, 4),                                 /* [5] struct */
+                               BTF_MEMBER_ENC(NAME_TBD, 1, 0),
+                       BTF_UNION_ENC(NAME_TBD, 1, 4),                                  /* [6] union */
+                               BTF_MEMBER_ENC(NAME_TBD, 1, 0),
+                       BTF_TYPEDEF_ENC(NAME_TBD, 1),                                   /* [7] typedef */
+                       BTF_PTR_ENC(0),                                                 /* [8] ptr */
+                       BTF_CONST_ENC(8),                                               /* [9] const */
+                       BTF_VOLATILE_ENC(8),                                            /* [10] volatile */
+                       BTF_RESTRICT_ENC(8),                                            /* [11] restrict */
+                       BTF_FUNC_PROTO_ENC(1, 2),                                       /* [12] func_proto */
+                               BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                               BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 8),
+                       BTF_FUNC_ENC(NAME_TBD, 12),                                     /* [13] func */
+                       BTF_END_RAW,
+               },
+               BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M"),
+       },
+       .opts = {
+               .dont_resolve_fwds = false,
+       },
+},
+{
+       .descr = "dedup: no int duplicates",
+       .input = {
+               .raw_types = {
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 8),
+                       /* different name */
+                       BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 32, 8),
+                       /* different encoding */
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_CHAR, 0, 32, 8),
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_BOOL, 0, 32, 8),
+                       /* different bit offset */
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 8, 32, 8),
+                       /* different bit size */
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 27, 8),
+                       /* different byte size */
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
+                       BTF_END_RAW,
+               },
+               BTF_STR_SEC("\0int\0some other int"),
+       },
+       .expect = {
+               .raw_types = {
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 8),
+                       /* different name */
+                       BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 32, 8),
+                       /* different encoding */
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_CHAR, 0, 32, 8),
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_BOOL, 0, 32, 8),
+                       /* different bit offset */
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 8, 32, 8),
+                       /* different bit size */
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 27, 8),
+                       /* different byte size */
+                       BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
+                       BTF_END_RAW,
+               },
+               BTF_STR_SEC("\0int\0some other int"),
+       },
+       .opts = {
+               .dont_resolve_fwds = false,
+       },
+},
+
+};
+
+static int btf_type_size(const struct btf_type *t)
+{
+       int base_size = sizeof(struct btf_type);
+       __u16 vlen = BTF_INFO_VLEN(t->info);
+       __u16 kind = BTF_INFO_KIND(t->info);
+
+       switch (kind) {
+       case BTF_KIND_FWD:
+       case BTF_KIND_CONST:
+       case BTF_KIND_VOLATILE:
+       case BTF_KIND_RESTRICT:
+       case BTF_KIND_PTR:
+       case BTF_KIND_TYPEDEF:
+       case BTF_KIND_FUNC:
+               return base_size;
+       case BTF_KIND_INT:
+               return base_size + sizeof(__u32);
+       case BTF_KIND_ENUM:
+               return base_size + vlen * sizeof(struct btf_enum);
+       case BTF_KIND_ARRAY:
+               return base_size + sizeof(struct btf_array);
+       case BTF_KIND_STRUCT:
+       case BTF_KIND_UNION:
+               return base_size + vlen * sizeof(struct btf_member);
+       case BTF_KIND_FUNC_PROTO:
+               return base_size + vlen * sizeof(struct btf_param);
+       default:
+               fprintf(stderr, "Unsupported BTF_KIND:%u\n", kind);
+               return -EINVAL;
+       }
+}
+
+static void dump_btf_strings(const char *strs, __u32 len)
+{
+       const char *cur = strs;
+       int i = 0;
+
+       while (cur < strs + len) {
+               fprintf(stderr, "string #%d: '%s'\n", i, cur);
+               cur += strlen(cur) + 1;
+               i++;
+       }
+}
+
+static int do_test_dedup(unsigned int test_num)
+{
+       const struct btf_dedup_test *test = &dedup_tests[test_num - 1];
+       __u32 test_nr_types, expect_nr_types, test_btf_size, expect_btf_size;
+       const struct btf_header *test_hdr, *expect_hdr;
+       struct btf *test_btf = NULL, *expect_btf = NULL;
+       const void *test_btf_data, *expect_btf_data;
+       const char *ret_test_next_str, *ret_expect_next_str;
+       const char *test_strs, *expect_strs;
+       const char *test_str_cur, *test_str_end;
+       const char *expect_str_cur, *expect_str_end;
+       unsigned int raw_btf_size;
+       void *raw_btf;
+       int err = 0, i;
+
+       fprintf(stderr, "BTF dedup test[%u] (%s):", test_num, test->descr);
+
+       raw_btf = btf_raw_create(&hdr_tmpl, test->input.raw_types,
+                                test->input.str_sec, test->input.str_sec_size,
+                                &raw_btf_size, &ret_test_next_str);
+       if (!raw_btf)
+               return -1;
+       test_btf = btf__new((__u8 *)raw_btf, raw_btf_size);
+       free(raw_btf);
+       if (CHECK(IS_ERR(test_btf), "invalid test_btf errno:%ld",
+                 PTR_ERR(test_btf))) {
+               err = -1;
+               goto done;
+       }
+
+       raw_btf = btf_raw_create(&hdr_tmpl, test->expect.raw_types,
+                                test->expect.str_sec,
+                                test->expect.str_sec_size,
+                                &raw_btf_size, &ret_expect_next_str);
+       if (!raw_btf)
+               return -1;
+       expect_btf = btf__new((__u8 *)raw_btf, raw_btf_size);
+       free(raw_btf);
+       if (CHECK(IS_ERR(expect_btf), "invalid expect_btf errno:%ld",
+                 PTR_ERR(expect_btf))) {
+               err = -1;
+               goto done;
+       }
+
+       err = btf__dedup(test_btf, NULL, &test->opts);
+       if (CHECK(err, "btf_dedup failed errno:%d", err)) {
+               err = -1;
+               goto done;
+       }
+
+       test_btf_data = btf__get_raw_data(test_btf, &test_btf_size);
+       expect_btf_data = btf__get_raw_data(expect_btf, &expect_btf_size);
+       if (CHECK(test_btf_size != expect_btf_size,
+                 "test_btf_size:%u != expect_btf_size:%u",
+                 test_btf_size, expect_btf_size)) {
+               err = -1;
+               goto done;
+       }
+
+       test_hdr = test_btf_data;
+       test_strs = test_btf_data + test_hdr->str_off;
+       expect_hdr = expect_btf_data;
+       expect_strs = expect_btf_data + expect_hdr->str_off;
+       if (CHECK(test_hdr->str_len != expect_hdr->str_len,
+                 "test_hdr->str_len:%u != expect_hdr->str_len:%u",
+                 test_hdr->str_len, expect_hdr->str_len)) {
+               fprintf(stderr, "\ntest strings:\n");
+               dump_btf_strings(test_strs, test_hdr->str_len);
+               fprintf(stderr, "\nexpected strings:\n");
+               dump_btf_strings(expect_strs, expect_hdr->str_len);
+               err = -1;
+               goto done;
+       }
+
+       test_str_cur = test_strs;
+       test_str_end = test_strs + test_hdr->str_len;
+       expect_str_cur = expect_strs;
+       expect_str_end = expect_strs + expect_hdr->str_len;
+       while (test_str_cur < test_str_end && expect_str_cur < expect_str_end) {
+               size_t test_len, expect_len;
+
+               test_len = strlen(test_str_cur);
+               expect_len = strlen(expect_str_cur);
+               if (CHECK(test_len != expect_len,
+                         "test_len:%zu != expect_len:%zu "
+                         "(test_str:%s, expect_str:%s)",
+                         test_len, expect_len, test_str_cur, expect_str_cur)) {
+                       err = -1;
+                       goto done;
+               }
+               if (CHECK(strcmp(test_str_cur, expect_str_cur),
+                         "test_str:%s != expect_str:%s",
+                         test_str_cur, expect_str_cur)) {
+                       err = -1;
+                       goto done;
+               }
+               test_str_cur += test_len + 1;
+               expect_str_cur += expect_len + 1;
+       }
+       if (CHECK(test_str_cur != test_str_end,
+                 "test_str_cur:%p != test_str_end:%p",
+                 test_str_cur, test_str_end)) {
+               err = -1;
+               goto done;
+       }
+
+       test_nr_types = btf__get_nr_types(test_btf);
+       expect_nr_types = btf__get_nr_types(expect_btf);
+       if (CHECK(test_nr_types != expect_nr_types,
+                 "test_nr_types:%u != expect_nr_types:%u",
+                 test_nr_types, expect_nr_types)) {
+               err = -1;
+               goto done;
+       }
+
+       for (i = 1; i <= test_nr_types; i++) {
+               const struct btf_type *test_type, *expect_type;
+               int test_size, expect_size;
+
+               test_type = btf__type_by_id(test_btf, i);
+               expect_type = btf__type_by_id(expect_btf, i);
+               test_size = btf_type_size(test_type);
+               expect_size = btf_type_size(expect_type);
+
+               if (CHECK(test_size != expect_size,
+                         "type #%d: test_size:%d != expect_size:%u",
+                         i, test_size, expect_size)) {
+                       err = -1;
+                       goto done;
+               }
+               if (CHECK(memcmp((void *)test_type,
+                                (void *)expect_type,
+                                test_size),
+                         "type #%d: contents differ", i)) {
+                       err = -1;
+                       goto done;
+               }
+       }
+
+done:
+       if (!err)
+               fprintf(stderr, "OK");
+       if (!IS_ERR(test_btf))
+               btf__free(test_btf);
+       if (!IS_ERR(expect_btf))
+               btf__free(expect_btf);
+
+       return err;
+}
+
+static int test_dedup(void)
+{
+       unsigned int i;
+       int err = 0;
+
+       if (args.dedup_test_num)
+               return count_result(do_test_dedup(args.dedup_test_num));
+
+       for (i = 1; i <= ARRAY_SIZE(dedup_tests); i++)
+               err |= count_result(do_test_dedup(i));
+
+       return err;
+}
+
 static void usage(const char *cmd)
 {
        fprintf(stderr, "Usage: %s [-l] [[-r btf_raw_test_num (1 - %zu)] |\n"
                        "\t[-g btf_get_info_test_num (1 - %zu)] |\n"
                        "\t[-f btf_file_test_num (1 - %zu)] |\n"
                        "\t[-k btf_prog_info_raw_test_num (1 - %zu)] |\n"
-                       "\t[-p (pretty print test)]]\n",
+                       "\t[-p (pretty print test)] |\n"
+                       "\t[-d btf_dedup_test_num (1 - %zu)]]\n",
                cmd, ARRAY_SIZE(raw_tests), ARRAY_SIZE(get_info_tests),
-               ARRAY_SIZE(file_tests), ARRAY_SIZE(info_raw_tests));
+               ARRAY_SIZE(file_tests), ARRAY_SIZE(info_raw_tests),
+               ARRAY_SIZE(dedup_tests));
 }
 
 static int parse_args(int argc, char **argv)
 {
-       const char *optstr = "lpk:f:r:g:";
+       const char *optstr = "hlpk:f:r:g:d:";
        int opt;
 
        while ((opt = getopt(argc, argv, optstr)) != -1) {
@@ -5591,12 +6082,16 @@ static int parse_args(int argc, char **argv)
                        args.info_raw_test_num = atoi(optarg);
                        args.info_raw_test = true;
                        break;
+               case 'd':
+                       args.dedup_test_num = atoi(optarg);
+                       args.dedup_test = true;
+                       break;
                case 'h':
                        usage(argv[0]);
                        exit(0);
                default:
-                               usage(argv[0]);
-                               return -1;
+                       usage(argv[0]);
+                       return -1;
                }
        }
 
@@ -5632,6 +6127,14 @@ static int parse_args(int argc, char **argv)
                return -1;
        }
 
+       if (args.dedup_test_num &&
+           (args.dedup_test_num < 1 ||
+            args.dedup_test_num > ARRAY_SIZE(dedup_tests))) {
+               fprintf(stderr, "BTF dedup test number must be [1 - %zu]\n",
+                       ARRAY_SIZE(dedup_tests));
+               return -1;
+       }
+
        return 0;
 }
 
@@ -5650,7 +6153,7 @@ int main(int argc, char **argv)
                return err;
 
        if (args.always_log)
-               libbpf_set_print(__base_pr, __base_pr, __base_pr);
+               libbpf_set_print(__base_pr);
 
        if (args.raw_test)
                err |= test_raw();
@@ -5667,14 +6170,18 @@ int main(int argc, char **argv)
        if (args.info_raw_test)
                err |= test_info_raw();
 
+       if (args.dedup_test)
+               err |= test_dedup();
+
        if (args.raw_test || args.get_info_test || args.file_test ||
-           args.pprint_test || args.info_raw_test)
+           args.pprint_test || args.info_raw_test || args.dedup_test)
                goto done;
 
        err |= test_raw();
        err |= test_get_info();
        err |= test_file();
        err |= test_info_raw();
+       err |= test_dedup();
 
 done:
        print_summary();
index 8fcd1c0..1909ecf 100644 (file)
@@ -34,23 +34,16 @@ static void usage(char *argv[])
        printf("\n");
 }
 
-#define DEFINE_PRINT_FN(name, enabled) \
-static int libbpf_##name(const char *fmt, ...)         \
-{                                                      \
-        va_list args;                                  \
-        int ret;                                       \
-                                                       \
-        va_start(args, fmt);                           \
-       if (enabled) {                                  \
-               fprintf(stderr, "[" #name "] ");        \
-               ret = vfprintf(stderr, fmt, args);      \
-       }                                               \
-        va_end(args);                                  \
-        return ret;                                    \
+static bool debug = 0;
+static int libbpf_debug_print(enum libbpf_print_level level,
+                             const char *fmt, va_list args)
+{
+       if (level == LIBBPF_DEBUG && !debug)
+               return 0;
+
+       fprintf(stderr, "[%d] ", level);
+       return vfprintf(stderr, fmt, args);
 }
-DEFINE_PRINT_FN(warning, 1)
-DEFINE_PRINT_FN(info, 1)
-DEFINE_PRINT_FN(debug, 1)
 
 #define EXIT_FAIL_LIBBPF EXIT_FAILURE
 #define EXIT_FAIL_OPTION 2
@@ -120,15 +113,14 @@ int main(int argc, char **argv)
        int longindex = 0;
        int opt;
 
-       libbpf_set_print(libbpf_warning, libbpf_info, NULL);
+       libbpf_set_print(libbpf_debug_print);
 
        /* Parse commands line args */
        while ((opt = getopt_long(argc, argv, "hDq",
                                  long_options, &longindex)) != -1) {
                switch (opt) {
                case 'D':
-                       libbpf_set_print(libbpf_warning, libbpf_info,
-                                        libbpf_debug);
+                       debug = 1;
                        break;
                case 'q': /* Use in scripting mode */
                        verbose = 0;
diff --git a/tools/testing/selftests/bpf/test_lwt_ip_encap.sh b/tools/testing/selftests/bpf/test_lwt_ip_encap.sh
new file mode 100755 (executable)
index 0000000..612632c
--- /dev/null
@@ -0,0 +1,376 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Setup/topology:
+#
+#    NS1             NS2             NS3
+#   veth1 <---> veth2   veth3 <---> veth4 (the top route)
+#   veth5 <---> veth6   veth7 <---> veth8 (the bottom route)
+#
+#   each vethN gets IPv[4|6]_N address
+#
+#   IPv*_SRC = IPv*_1
+#   IPv*_DST = IPv*_4
+#
+#   all tests test pings from IPv*_SRC to IPv*_DST
+#
+#   by default, routes are configured to allow packets to go
+#   IP*_1 <=> IP*_2 <=> IP*_3 <=> IP*_4 (the top route)
+#
+#   a GRE device is installed in NS3 with IPv*_GRE, and
+#   NS1/NS2 are configured to route packets to IPv*_GRE via IP*_8
+#   (the bottom route)
+#
+# Tests:
+#
+#   1. routes NS2->IPv*_DST are brought down, so the only way a ping
+#      from IP*_SRC to IP*_DST can work is via IPv*_GRE
+#
+#   2a. in an egress test, a bpf LWT_XMIT program is installed on veth1
+#       that encaps the packets with an IP/GRE header to route to IPv*_GRE
+#
+#       ping: SRC->[encap at veth1:egress]->GRE:decap->DST
+#       ping replies go DST->SRC directly
+#
+#   2b. in an ingress test, a bpf LWT_IN program is installed on veth2
+#       that encaps the packets with an IP/GRE header to route to IPv*_GRE
+#
+#       ping: SRC->[encap at veth2:ingress]->GRE:decap->DST
+#       ping replies go DST->SRC directly
+
+if [[ $EUID -ne 0 ]]; then
+       echo "This script must be run as root"
+       echo "FAIL"
+       exit 1
+fi
+
+readonly NS1="ns1-$(mktemp -u XXXXXX)"
+readonly NS2="ns2-$(mktemp -u XXXXXX)"
+readonly NS3="ns3-$(mktemp -u XXXXXX)"
+
+readonly IPv4_1="172.16.1.100"
+readonly IPv4_2="172.16.2.100"
+readonly IPv4_3="172.16.3.100"
+readonly IPv4_4="172.16.4.100"
+readonly IPv4_5="172.16.5.100"
+readonly IPv4_6="172.16.6.100"
+readonly IPv4_7="172.16.7.100"
+readonly IPv4_8="172.16.8.100"
+readonly IPv4_GRE="172.16.16.100"
+
+readonly IPv4_SRC=$IPv4_1
+readonly IPv4_DST=$IPv4_4
+
+readonly IPv6_1="fb01::1"
+readonly IPv6_2="fb02::1"
+readonly IPv6_3="fb03::1"
+readonly IPv6_4="fb04::1"
+readonly IPv6_5="fb05::1"
+readonly IPv6_6="fb06::1"
+readonly IPv6_7="fb07::1"
+readonly IPv6_8="fb08::1"
+readonly IPv6_GRE="fb10::1"
+
+readonly IPv6_SRC=$IPv6_1
+readonly IPv6_DST=$IPv6_4
+
+TEST_STATUS=0
+TESTS_SUCCEEDED=0
+TESTS_FAILED=0
+
+process_test_results()
+{
+       if [[ "${TEST_STATUS}" -eq 0 ]] ; then
+               echo "PASS"
+               TESTS_SUCCEEDED=$((TESTS_SUCCEEDED+1))
+       else
+               echo "FAIL"
+               TESTS_FAILED=$((TESTS_FAILED+1))
+       fi
+}
+
+print_test_summary_and_exit()
+{
+       echo "passed tests: ${TESTS_SUCCEEDED}"
+       echo "failed tests: ${TESTS_FAILED}"
+       if [ "${TESTS_FAILED}" -eq "0" ] ; then
+               exit 0
+       else
+               exit 1
+       fi
+}
+
+setup()
+{
+       set -e  # exit on error
+       TEST_STATUS=0
+
+       # create devices and namespaces
+       ip netns add "${NS1}"
+       ip netns add "${NS2}"
+       ip netns add "${NS3}"
+
+       ip link add veth1 type veth peer name veth2
+       ip link add veth3 type veth peer name veth4
+       ip link add veth5 type veth peer name veth6
+       ip link add veth7 type veth peer name veth8
+
+       ip netns exec ${NS2} sysctl -wq net.ipv4.ip_forward=1
+       ip netns exec ${NS2} sysctl -wq net.ipv6.conf.all.forwarding=1
+
+       ip link set veth1 netns ${NS1}
+       ip link set veth2 netns ${NS2}
+       ip link set veth3 netns ${NS2}
+       ip link set veth4 netns ${NS3}
+       ip link set veth5 netns ${NS1}
+       ip link set veth6 netns ${NS2}
+       ip link set veth7 netns ${NS2}
+       ip link set veth8 netns ${NS3}
+
+       # configure addesses: the top route (1-2-3-4)
+       ip -netns ${NS1}    addr add ${IPv4_1}/24  dev veth1
+       ip -netns ${NS2}    addr add ${IPv4_2}/24  dev veth2
+       ip -netns ${NS2}    addr add ${IPv4_3}/24  dev veth3
+       ip -netns ${NS3}    addr add ${IPv4_4}/24  dev veth4
+       ip -netns ${NS1} -6 addr add ${IPv6_1}/128 nodad dev veth1
+       ip -netns ${NS2} -6 addr add ${IPv6_2}/128 nodad dev veth2
+       ip -netns ${NS2} -6 addr add ${IPv6_3}/128 nodad dev veth3
+       ip -netns ${NS3} -6 addr add ${IPv6_4}/128 nodad dev veth4
+
+       # configure addresses: the bottom route (5-6-7-8)
+       ip -netns ${NS1}    addr add ${IPv4_5}/24  dev veth5
+       ip -netns ${NS2}    addr add ${IPv4_6}/24  dev veth6
+       ip -netns ${NS2}    addr add ${IPv4_7}/24  dev veth7
+       ip -netns ${NS3}    addr add ${IPv4_8}/24  dev veth8
+       ip -netns ${NS1} -6 addr add ${IPv6_5}/128 nodad dev veth5
+       ip -netns ${NS2} -6 addr add ${IPv6_6}/128 nodad dev veth6
+       ip -netns ${NS2} -6 addr add ${IPv6_7}/128 nodad dev veth7
+       ip -netns ${NS3} -6 addr add ${IPv6_8}/128 nodad dev veth8
+
+
+       ip -netns ${NS1} link set dev veth1 up
+       ip -netns ${NS2} link set dev veth2 up
+       ip -netns ${NS2} link set dev veth3 up
+       ip -netns ${NS3} link set dev veth4 up
+       ip -netns ${NS1} link set dev veth5 up
+       ip -netns ${NS2} link set dev veth6 up
+       ip -netns ${NS2} link set dev veth7 up
+       ip -netns ${NS3} link set dev veth8 up
+
+       # configure routes: IP*_SRC -> veth1/IP*_2 (= top route) default;
+       # the bottom route to specific bottom addresses
+
+       # NS1
+       # top route
+       ip -netns ${NS1}    route add ${IPv4_2}/32  dev veth1
+       ip -netns ${NS1}    route add default dev veth1 via ${IPv4_2}  # go top by default
+       ip -netns ${NS1} -6 route add ${IPv6_2}/128 dev veth1
+       ip -netns ${NS1} -6 route add default dev veth1 via ${IPv6_2}  # go top by default
+       # bottom route
+       ip -netns ${NS1}    route add ${IPv4_6}/32  dev veth5
+       ip -netns ${NS1}    route add ${IPv4_7}/32  dev veth5 via ${IPv4_6}
+       ip -netns ${NS1}    route add ${IPv4_8}/32  dev veth5 via ${IPv4_6}
+       ip -netns ${NS1} -6 route add ${IPv6_6}/128 dev veth5
+       ip -netns ${NS1} -6 route add ${IPv6_7}/128 dev veth5 via ${IPv6_6}
+       ip -netns ${NS1} -6 route add ${IPv6_8}/128 dev veth5 via ${IPv6_6}
+
+       # NS2
+       # top route
+       ip -netns ${NS2}    route add ${IPv4_1}/32  dev veth2
+       ip -netns ${NS2}    route add ${IPv4_4}/32  dev veth3
+       ip -netns ${NS2} -6 route add ${IPv6_1}/128 dev veth2
+       ip -netns ${NS2} -6 route add ${IPv6_4}/128 dev veth3
+       # bottom route
+       ip -netns ${NS2}    route add ${IPv4_5}/32  dev veth6
+       ip -netns ${NS2}    route add ${IPv4_8}/32  dev veth7
+       ip -netns ${NS2} -6 route add ${IPv6_5}/128 dev veth6
+       ip -netns ${NS2} -6 route add ${IPv6_8}/128 dev veth7
+
+       # NS3
+       # top route
+       ip -netns ${NS3}    route add ${IPv4_3}/32  dev veth4
+       ip -netns ${NS3}    route add ${IPv4_1}/32  dev veth4 via ${IPv4_3}
+       ip -netns ${NS3}    route add ${IPv4_2}/32  dev veth4 via ${IPv4_3}
+       ip -netns ${NS3} -6 route add ${IPv6_3}/128 dev veth4
+       ip -netns ${NS3} -6 route add ${IPv6_1}/128 dev veth4 via ${IPv6_3}
+       ip -netns ${NS3} -6 route add ${IPv6_2}/128 dev veth4 via ${IPv6_3}
+       # bottom route
+       ip -netns ${NS3}    route add ${IPv4_7}/32  dev veth8
+       ip -netns ${NS3}    route add ${IPv4_5}/32  dev veth8 via ${IPv4_7}
+       ip -netns ${NS3}    route add ${IPv4_6}/32  dev veth8 via ${IPv4_7}
+       ip -netns ${NS3} -6 route add ${IPv6_7}/128 dev veth8
+       ip -netns ${NS3} -6 route add ${IPv6_5}/128 dev veth8 via ${IPv6_7}
+       ip -netns ${NS3} -6 route add ${IPv6_6}/128 dev veth8 via ${IPv6_7}
+
+       # configure IPv4 GRE device in NS3, and a route to it via the "bottom" route
+       ip -netns ${NS3} tunnel add gre_dev mode gre remote ${IPv4_1} local ${IPv4_GRE} ttl 255
+       ip -netns ${NS3} link set gre_dev up
+       ip -netns ${NS3} addr add ${IPv4_GRE} nodad dev gre_dev
+       ip -netns ${NS1} route add ${IPv4_GRE}/32 dev veth5 via ${IPv4_6}
+       ip -netns ${NS2} route add ${IPv4_GRE}/32 dev veth7 via ${IPv4_8}
+
+
+       # configure IPv6 GRE device in NS3, and a route to it via the "bottom" route
+       ip -netns ${NS3} -6 tunnel add name gre6_dev mode ip6gre remote ${IPv6_1} local ${IPv6_GRE} ttl 255
+       ip -netns ${NS3} link set gre6_dev up
+       ip -netns ${NS3} -6 addr add ${IPv6_GRE} nodad dev gre6_dev
+       ip -netns ${NS1} -6 route add ${IPv6_GRE}/128 dev veth5 via ${IPv6_6}
+       ip -netns ${NS2} -6 route add ${IPv6_GRE}/128 dev veth7 via ${IPv6_8}
+
+       # rp_filter gets confused by what these tests are doing, so disable it
+       ip netns exec ${NS1} sysctl -wq net.ipv4.conf.all.rp_filter=0
+       ip netns exec ${NS2} sysctl -wq net.ipv4.conf.all.rp_filter=0
+       ip netns exec ${NS3} sysctl -wq net.ipv4.conf.all.rp_filter=0
+
+       sleep 1  # reduce flakiness
+       set +e
+}
+
+cleanup()
+{
+       ip netns del ${NS1} 2> /dev/null
+       ip netns del ${NS2} 2> /dev/null
+       ip netns del ${NS3} 2> /dev/null
+}
+
+trap cleanup EXIT
+
+remove_routes_to_gredev()
+{
+       ip -netns ${NS1} route del ${IPv4_GRE} dev veth5
+       ip -netns ${NS2} route del ${IPv4_GRE} dev veth7
+       ip -netns ${NS1} -6 route del ${IPv6_GRE}/128 dev veth5
+       ip -netns ${NS2} -6 route del ${IPv6_GRE}/128 dev veth7
+}
+
+add_unreachable_routes_to_gredev()
+{
+       ip -netns ${NS1} route add unreachable ${IPv4_GRE}/32
+       ip -netns ${NS2} route add unreachable ${IPv4_GRE}/32
+       ip -netns ${NS1} -6 route add unreachable ${IPv6_GRE}/128
+       ip -netns ${NS2} -6 route add unreachable ${IPv6_GRE}/128
+}
+
+test_ping()
+{
+       local readonly PROTO=$1
+       local readonly EXPECTED=$2
+       local RET=0
+
+       if [ "${PROTO}" == "IPv4" ] ; then
+               ip netns exec ${NS1} ping  -c 1 -W 1 -I ${IPv4_SRC} ${IPv4_DST} 2>&1 > /dev/null
+               RET=$?
+       elif [ "${PROTO}" == "IPv6" ] ; then
+               ip netns exec ${NS1} ping6 -c 1 -W 6 -I ${IPv6_SRC} ${IPv6_DST} 2>&1 > /dev/null
+               RET=$?
+       else
+               echo "    test_ping: unknown PROTO: ${PROTO}"
+               TEST_STATUS=1
+       fi
+
+       if [ "0" != "${RET}" ]; then
+               RET=1
+       fi
+
+       if [ "${EXPECTED}" != "${RET}" ] ; then
+               echo "    test_ping failed: expected: ${EXPECTED}; got ${RET}"
+               TEST_STATUS=1
+       fi
+}
+
+test_egress()
+{
+       local readonly ENCAP=$1
+       echo "starting egress ${ENCAP} encap test"
+       setup
+
+       # by default, pings work
+       test_ping IPv4 0
+       test_ping IPv6 0
+
+       # remove NS2->DST routes, ping fails
+       ip -netns ${NS2}    route del ${IPv4_DST}/32  dev veth3
+       ip -netns ${NS2} -6 route del ${IPv6_DST}/128 dev veth3
+       test_ping IPv4 1
+       test_ping IPv6 1
+
+       # install replacement routes (LWT/eBPF), pings succeed
+       if [ "${ENCAP}" == "IPv4" ] ; then
+               ip -netns ${NS1} route add ${IPv4_DST} encap bpf xmit obj test_lwt_ip_encap.o sec encap_gre dev veth1
+               ip -netns ${NS1} -6 route add ${IPv6_DST} encap bpf xmit obj test_lwt_ip_encap.o sec encap_gre dev veth1
+       elif [ "${ENCAP}" == "IPv6" ] ; then
+               ip -netns ${NS1} route add ${IPv4_DST} encap bpf xmit obj test_lwt_ip_encap.o sec encap_gre6 dev veth1
+               ip -netns ${NS1} -6 route add ${IPv6_DST} encap bpf xmit obj test_lwt_ip_encap.o sec encap_gre6 dev veth1
+       else
+               echo "    unknown encap ${ENCAP}"
+               TEST_STATUS=1
+       fi
+       test_ping IPv4 0
+       test_ping IPv6 0
+
+       # a negative test: remove routes to GRE devices: ping fails
+       remove_routes_to_gredev
+       test_ping IPv4 1
+       test_ping IPv6 1
+
+       # another negative test
+       add_unreachable_routes_to_gredev
+       test_ping IPv4 1
+       test_ping IPv6 1
+
+       cleanup
+       process_test_results
+}
+
+test_ingress()
+{
+       local readonly ENCAP=$1
+       echo "starting ingress ${ENCAP} encap test"
+       setup
+
+       # need to wait a bit for IPv6 to autoconf, otherwise
+       # ping6 sometimes fails with "unable to bind to address"
+
+       # by default, pings work
+       test_ping IPv4 0
+       test_ping IPv6 0
+
+       # remove NS2->DST routes, pings fail
+       ip -netns ${NS2}    route del ${IPv4_DST}/32  dev veth3
+       ip -netns ${NS2} -6 route del ${IPv6_DST}/128 dev veth3
+       test_ping IPv4 1
+       test_ping IPv6 1
+
+       # install replacement routes (LWT/eBPF), pings succeed
+       if [ "${ENCAP}" == "IPv4" ] ; then
+               ip -netns ${NS2} route add ${IPv4_DST} encap bpf in obj test_lwt_ip_encap.o sec encap_gre dev veth2
+               ip -netns ${NS2} -6 route add ${IPv6_DST} encap bpf in obj test_lwt_ip_encap.o sec encap_gre dev veth2
+       elif [ "${ENCAP}" == "IPv6" ] ; then
+               ip -netns ${NS2} route add ${IPv4_DST} encap bpf in obj test_lwt_ip_encap.o sec encap_gre6 dev veth2
+               ip -netns ${NS2} -6 route add ${IPv6_DST} encap bpf in obj test_lwt_ip_encap.o sec encap_gre6 dev veth2
+       else
+               echo "FAIL: unknown encap ${ENCAP}"
+       fi
+       test_ping IPv4 0
+       test_ping IPv6 0
+
+       # a negative test: remove routes to GRE devices: ping fails
+       remove_routes_to_gredev
+       test_ping IPv4 1
+       test_ping IPv6 1
+
+       # another negative test
+       add_unreachable_routes_to_gredev
+       test_ping IPv4 1
+       test_ping IPv6 1
+
+       cleanup
+       process_test_results
+}
+
+test_egress IPv4
+test_egress IPv6
+test_ingress IPv4
+test_ingress IPv6
+
+print_test_summary_and_exit
index 1dfef77..3c62777 100644 (file)
@@ -32,6 +32,8 @@
 #define ENOTSUPP 524
 #endif
 
+static int skips;
+
 static int map_flags;
 
 #define CHECK(condition, tag, format...) ({                            \
@@ -43,7 +45,7 @@ static int map_flags;
        }                                                               \
 })
 
-static void test_hashmap(int task, void *data)
+static void test_hashmap(unsigned int task, void *data)
 {
        long long key, next_key, first_key, value;
        int fd;
@@ -133,7 +135,7 @@ static void test_hashmap(int task, void *data)
        close(fd);
 }
 
-static void test_hashmap_sizes(int task, void *data)
+static void test_hashmap_sizes(unsigned int task, void *data)
 {
        int fd, i, j;
 
@@ -153,7 +155,7 @@ static void test_hashmap_sizes(int task, void *data)
                }
 }
 
-static void test_hashmap_percpu(int task, void *data)
+static void test_hashmap_percpu(unsigned int task, void *data)
 {
        unsigned int nr_cpus = bpf_num_possible_cpus();
        BPF_DECLARE_PERCPU(long, value);
@@ -280,7 +282,7 @@ static int helper_fill_hashmap(int max_entries)
        return fd;
 }
 
-static void test_hashmap_walk(int task, void *data)
+static void test_hashmap_walk(unsigned int task, void *data)
 {
        int fd, i, max_entries = 1000;
        long long key, value, next_key;
@@ -351,7 +353,7 @@ static void test_hashmap_zero_seed(void)
        close(second);
 }
 
-static void test_arraymap(int task, void *data)
+static void test_arraymap(unsigned int task, void *data)
 {
        int key, next_key, fd;
        long long value;
@@ -406,7 +408,7 @@ static void test_arraymap(int task, void *data)
        close(fd);
 }
 
-static void test_arraymap_percpu(int task, void *data)
+static void test_arraymap_percpu(unsigned int task, void *data)
 {
        unsigned int nr_cpus = bpf_num_possible_cpus();
        BPF_DECLARE_PERCPU(long, values);
@@ -502,7 +504,7 @@ static void test_arraymap_percpu_many_keys(void)
        close(fd);
 }
 
-static void test_devmap(int task, void *data)
+static void test_devmap(unsigned int task, void *data)
 {
        int fd;
        __u32 key, value;
@@ -517,7 +519,7 @@ static void test_devmap(int task, void *data)
        close(fd);
 }
 
-static void test_queuemap(int task, void *data)
+static void test_queuemap(unsigned int task, void *data)
 {
        const int MAP_SIZE = 32;
        __u32 vals[MAP_SIZE + MAP_SIZE/2], val;
@@ -575,7 +577,7 @@ static void test_queuemap(int task, void *data)
        close(fd);
 }
 
-static void test_stackmap(int task, void *data)
+static void test_stackmap(unsigned int task, void *data)
 {
        const int MAP_SIZE = 32;
        __u32 vals[MAP_SIZE + MAP_SIZE/2], val;
@@ -640,7 +642,7 @@ static void test_stackmap(int task, void *data)
 #define SOCKMAP_PARSE_PROG "./sockmap_parse_prog.o"
 #define SOCKMAP_VERDICT_PROG "./sockmap_verdict_prog.o"
 #define SOCKMAP_TCP_MSG_PROG "./sockmap_tcp_msg_prog.o"
-static void test_sockmap(int tasks, void *data)
+static void test_sockmap(unsigned int tasks, void *data)
 {
        struct bpf_map *bpf_map_rx, *bpf_map_tx, *bpf_map_msg, *bpf_map_break;
        int map_fd_msg = 0, map_fd_rx = 0, map_fd_tx = 0, map_fd_break;
@@ -724,6 +726,15 @@ static void test_sockmap(int tasks, void *data)
                            sizeof(key), sizeof(value),
                            6, 0);
        if (fd < 0) {
+               if (!bpf_probe_map_type(BPF_MAP_TYPE_SOCKMAP, 0)) {
+                       printf("%s SKIP (unsupported map type BPF_MAP_TYPE_SOCKMAP)\n",
+                              __func__);
+                       skips++;
+                       for (i = 0; i < 6; i++)
+                               close(sfd[i]);
+                       return;
+               }
+
                printf("Failed to create sockmap %i\n", fd);
                goto out_sockmap;
        }
@@ -1257,10 +1268,11 @@ static void test_map_large(void)
 }
 
 #define run_parallel(N, FN, DATA) \
-       printf("Fork %d tasks to '" #FN "'\n", N); \
+       printf("Fork %u tasks to '" #FN "'\n", N); \
        __run_parallel(N, FN, DATA)
 
-static void __run_parallel(int tasks, void (*fn)(int task, void *data),
+static void __run_parallel(unsigned int tasks,
+                          void (*fn)(unsigned int task, void *data),
                           void *data)
 {
        pid_t pid[tasks];
@@ -1301,7 +1313,7 @@ static void test_map_stress(void)
 #define DO_UPDATE 1
 #define DO_DELETE 0
 
-static void test_update_delete(int fn, void *data)
+static void test_update_delete(unsigned int fn, void *data)
 {
        int do_update = ((int *)data)[1];
        int fd = ((int *)data)[0];
@@ -1701,6 +1713,6 @@ int main(void)
        map_flags = BPF_F_NO_PREALLOC;
        run_all_tests();
 
-       printf("test_maps: OK\n");
+       printf("test_maps: OK, %d SKIPPED\n", skips);
        return 0;
 }
index d59642e..84bea39 100755 (executable)
@@ -23,6 +23,7 @@ import string
 import struct
 import subprocess
 import time
+import traceback
 
 logfile = None
 log_level = 1
@@ -78,7 +79,9 @@ def fail(cond, msg):
     if not cond:
         return
     print("FAIL: " + msg)
-    log("FAIL: " + msg, "", level=1)
+    tb = "".join(traceback.extract_stack().format())
+    print(tb)
+    log("FAIL: " + msg, tb, level=1)
     os.sys.exit(1)
 
 def start_test(msg):
@@ -589,6 +592,15 @@ def check_verifier_log(output, reference):
             return
     fail(True, "Missing or incorrect message from netdevsim in verifier log")
 
+def check_multi_basic(two_xdps):
+    fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
+    fail("prog" in two_xdps, "Base program reported in multi program mode")
+    fail(len(two_xdps["attached"]) != 2,
+         "Wrong attached program count with two programs")
+    fail(two_xdps["attached"][0]["prog"]["id"] ==
+         two_xdps["attached"][1]["prog"]["id"],
+         "Offloaded and other programs have the same id")
+
 def test_spurios_extack(sim, obj, skip_hw, needle):
     res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
                                  include_stderr=True)
@@ -600,6 +612,67 @@ def test_spurios_extack(sim, obj, skip_hw, needle):
                             include_stderr=True)
     check_no_extack(res, needle)
 
+def test_multi_prog(sim, obj, modename, modeid):
+    start_test("Test multi-attachment XDP - %s + offload..." %
+               (modename or "default", ))
+    sim.set_xdp(obj, "offload")
+    xdp = sim.ip_link_show(xdp=True)["xdp"]
+    offloaded = sim.dfs_read("bpf_offloaded_id")
+    fail("prog" not in xdp, "Base program not reported in single program mode")
+    fail(len(xdp["attached"]) != 1,
+         "Wrong attached program count with one program")
+
+    sim.set_xdp(obj, modename)
+    two_xdps = sim.ip_link_show(xdp=True)["xdp"]
+
+    fail(xdp["attached"][0] not in two_xdps["attached"],
+         "Offload program not reported after other activated")
+    check_multi_basic(two_xdps)
+
+    offloaded2 = sim.dfs_read("bpf_offloaded_id")
+    fail(offloaded != offloaded2,
+         "Offload ID changed after loading other program")
+
+    start_test("Test multi-attachment XDP - replace...")
+    ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
+    fail(ret == 0, "Replaced one of programs without -force")
+    check_extack(err, "XDP program already attached.", args)
+
+    if modename == "" or modename == "drv":
+        othermode = "" if modename == "drv" else "drv"
+        start_test("Test multi-attachment XDP - detach...")
+        ret, _, err = sim.unset_xdp(othermode, force=True,
+                                    fail=False, include_stderr=True)
+        fail(ret == 0, "Removed program with a bad mode")
+        check_extack(err, "program loaded with different flags.", args)
+
+    sim.unset_xdp("offload")
+    xdp = sim.ip_link_show(xdp=True)["xdp"]
+    offloaded = sim.dfs_read("bpf_offloaded_id")
+
+    fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs")
+    fail("prog" not in xdp,
+         "Base program not reported after multi program mode")
+    fail(xdp["attached"][0] not in two_xdps["attached"],
+         "Offload program not reported after other activated")
+    fail(len(xdp["attached"]) != 1,
+         "Wrong attached program count with remaining programs")
+    fail(offloaded != "0", "Offload ID reported with only other program left")
+
+    start_test("Test multi-attachment XDP - reattach...")
+    sim.set_xdp(obj, "offload")
+    two_xdps = sim.ip_link_show(xdp=True)["xdp"]
+
+    fail(xdp["attached"][0] not in two_xdps["attached"],
+         "Other program not reported after offload activated")
+    check_multi_basic(two_xdps)
+
+    start_test("Test multi-attachment XDP - device remove...")
+    sim.remove()
+
+    sim = NetdevSim()
+    sim.set_ethtool_tc_offloads(True)
+    return sim
 
 # Parse command line
 parser = argparse.ArgumentParser()
@@ -842,7 +915,9 @@ try:
     ret, _, err = sim.set_xdp(obj, "generic", force=True,
                               fail=False, include_stderr=True)
     fail(ret == 0, "Replaced XDP program with a program in different mode")
-    fail(err.count("File exists") != 1, "Replaced driver XDP with generic")
+    check_extack(err,
+                 "native and generic XDP can't be active at the same time.",
+                 args)
     ret, _, err = sim.set_xdp(obj, "", force=True,
                               fail=False, include_stderr=True)
     fail(ret == 0, "Replaced XDP program with a program in different mode")
@@ -931,59 +1006,9 @@ try:
     rm(pin_file)
     bpftool_prog_list_wait(expected=0)
 
-    start_test("Test multi-attachment XDP - attach...")
-    sim.set_xdp(obj, "offload")
-    xdp = sim.ip_link_show(xdp=True)["xdp"]
-    offloaded = sim.dfs_read("bpf_offloaded_id")
-    fail("prog" not in xdp, "Base program not reported in single program mode")
-    fail(len(ipl["xdp"]["attached"]) != 1,
-         "Wrong attached program count with one program")
-
-    sim.set_xdp(obj, "")
-    two_xdps = sim.ip_link_show(xdp=True)["xdp"]
-    offloaded2 = sim.dfs_read("bpf_offloaded_id")
-
-    fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
-    fail("prog" in two_xdps, "Base program reported in multi program mode")
-    fail(xdp["attached"][0] not in two_xdps["attached"],
-         "Offload program not reported after driver activated")
-    fail(len(two_xdps["attached"]) != 2,
-         "Wrong attached program count with two programs")
-    fail(two_xdps["attached"][0]["prog"]["id"] ==
-         two_xdps["attached"][1]["prog"]["id"],
-         "offloaded and drv programs have the same id")
-    fail(offloaded != offloaded2,
-         "offload ID changed after loading driver program")
-
-    start_test("Test multi-attachment XDP - replace...")
-    ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
-    fail(err.count("busy") != 1, "Replaced one of programs without -force")
-
-    start_test("Test multi-attachment XDP - detach...")
-    ret, _, err = sim.unset_xdp("drv", force=True,
-                                fail=False, include_stderr=True)
-    fail(ret == 0, "Removed program with a bad mode")
-    check_extack(err, "program loaded with different flags.", args)
-
-    sim.unset_xdp("offload")
-    xdp = sim.ip_link_show(xdp=True)["xdp"]
-    offloaded = sim.dfs_read("bpf_offloaded_id")
-
-    fail(xdp["mode"] != 1, "Bad mode reported after multiple programs")
-    fail("prog" not in xdp,
-         "Base program not reported after multi program mode")
-    fail(xdp["attached"][0] not in two_xdps["attached"],
-         "Offload program not reported after driver activated")
-    fail(len(ipl["xdp"]["attached"]) != 1,
-         "Wrong attached program count with remaining programs")
-    fail(offloaded != "0", "offload ID reported with only driver program left")
-
-    start_test("Test multi-attachment XDP - device remove...")
-    sim.set_xdp(obj, "offload")
-    sim.remove()
-
-    sim = NetdevSim()
-    sim.set_ethtool_tc_offloads(True)
+    sim = test_multi_prog(sim, obj, "", 1)
+    sim = test_multi_prog(sim, obj, "drv", 1)
+    sim = test_multi_prog(sim, obj, "generic", 2)
 
     start_test("Test mixing of TC and XDP...")
     sim.tc_add_ingress()
index d8940b8..c52bd90 100644 (file)
@@ -10,6 +10,7 @@
 #include <string.h>
 #include <assert.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <time.h>
 
 #include <linux/types.h>
@@ -28,7 +29,7 @@ typedef __u16 __sum16;
 #include <sys/wait.h>
 #include <sys/types.h>
 #include <fcntl.h>
-
+#include <pthread.h>
 #include <linux/bpf.h>
 #include <linux/err.h>
 #include <bpf/bpf.h>
@@ -1783,6 +1784,15 @@ static void test_task_fd_query_tp(void)
                                   "sys_enter_read");
 }
 
+static int libbpf_debug_print(enum libbpf_print_level level,
+                             const char *format, va_list args)
+{
+       if (level == LIBBPF_DEBUG)
+               return 0;
+
+       return vfprintf(stderr, format, args);
+}
+
 static void test_reference_tracking()
 {
        const char *file = "./test_sk_lookup_kern.o";
@@ -1809,9 +1819,9 @@ static void test_reference_tracking()
 
                /* Expect verifier failure if test name has 'fail' */
                if (strstr(title, "fail") != NULL) {
-                       libbpf_set_print(NULL, NULL, NULL);
+                       libbpf_set_print(NULL);
                        err = !bpf_program__load(prog, "GPL", 0);
-                       libbpf_set_print(printf, printf, NULL);
+                       libbpf_set_print(libbpf_debug_print);
                } else {
                        err = bpf_program__load(prog, "GPL", 0);
                }
@@ -1985,6 +1995,119 @@ static void test_flow_dissector(void)
        bpf_object__close(obj);
 }
 
+static void *test_spin_lock(void *arg)
+{
+       __u32 duration, retval;
+       int err, prog_fd = *(u32 *) arg;
+
+       err = bpf_prog_test_run(prog_fd, 10000, &pkt_v4, sizeof(pkt_v4),
+                               NULL, NULL, &retval, &duration);
+       CHECK(err || retval, "",
+             "err %d errno %d retval %d duration %d\n",
+             err, errno, retval, duration);
+       pthread_exit(arg);
+}
+
+static void test_spinlock(void)
+{
+       const char *file = "./test_spin_lock.o";
+       pthread_t thread_id[4];
+       struct bpf_object *obj;
+       int prog_fd;
+       int err = 0, i;
+       void *ret;
+
+       err = bpf_prog_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd);
+       if (err) {
+               printf("test_spin_lock:bpf_prog_load errno %d\n", errno);
+               goto close_prog;
+       }
+       for (i = 0; i < 4; i++)
+               assert(pthread_create(&thread_id[i], NULL,
+                                     &test_spin_lock, &prog_fd) == 0);
+       for (i = 0; i < 4; i++)
+               assert(pthread_join(thread_id[i], &ret) == 0 &&
+                      ret == (void *)&prog_fd);
+       goto close_prog_noerr;
+close_prog:
+       error_cnt++;
+close_prog_noerr:
+       bpf_object__close(obj);
+}
+
+static void *parallel_map_access(void *arg)
+{
+       int err, map_fd = *(u32 *) arg;
+       int vars[17], i, j, rnd, key = 0;
+
+       for (i = 0; i < 10000; i++) {
+               err = bpf_map_lookup_elem_flags(map_fd, &key, vars, BPF_F_LOCK);
+               if (err) {
+                       printf("lookup failed\n");
+                       error_cnt++;
+                       goto out;
+               }
+               if (vars[0] != 0) {
+                       printf("lookup #%d var[0]=%d\n", i, vars[0]);
+                       error_cnt++;
+                       goto out;
+               }
+               rnd = vars[1];
+               for (j = 2; j < 17; j++) {
+                       if (vars[j] == rnd)
+                               continue;
+                       printf("lookup #%d var[1]=%d var[%d]=%d\n",
+                              i, rnd, j, vars[j]);
+                       error_cnt++;
+                       goto out;
+               }
+       }
+out:
+       pthread_exit(arg);
+}
+
+static void test_map_lock(void)
+{
+       const char *file = "./test_map_lock.o";
+       int prog_fd, map_fd[2], vars[17] = {};
+       pthread_t thread_id[6];
+       struct bpf_object *obj;
+       int err = 0, key = 0, i;
+       void *ret;
+
+       err = bpf_prog_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd);
+       if (err) {
+               printf("test_map_lock:bpf_prog_load errno %d\n", errno);
+               goto close_prog;
+       }
+       map_fd[0] = bpf_find_map(__func__, obj, "hash_map");
+       if (map_fd[0] < 0)
+               goto close_prog;
+       map_fd[1] = bpf_find_map(__func__, obj, "array_map");
+       if (map_fd[1] < 0)
+               goto close_prog;
+
+       bpf_map_update_elem(map_fd[0], &key, vars, BPF_F_LOCK);
+
+       for (i = 0; i < 4; i++)
+               assert(pthread_create(&thread_id[i], NULL,
+                                     &test_spin_lock, &prog_fd) == 0);
+       for (i = 4; i < 6; i++)
+               assert(pthread_create(&thread_id[i], NULL,
+                                     &parallel_map_access, &map_fd[i - 4]) == 0);
+       for (i = 0; i < 4; i++)
+               assert(pthread_join(thread_id[i], &ret) == 0 &&
+                      ret == (void *)&prog_fd);
+       for (i = 4; i < 6; i++)
+               assert(pthread_join(thread_id[i], &ret) == 0 &&
+                      ret == (void *)&map_fd[i - 4]);
+       goto close_prog_noerr;
+close_prog:
+       error_cnt++;
+close_prog_noerr:
+       bpf_object__close(obj);
+}
+
 int main(void)
 {
        srand(time(NULL));
@@ -2013,6 +2136,8 @@ int main(void)
        test_queue_stack_map(QUEUE);
        test_queue_stack_map(STACK);
        test_flow_dissector();
+       test_spinlock();
+       test_map_lock();
 
        printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt);
        return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
index 561ffb6..fb679ac 100644 (file)
@@ -20,6 +20,7 @@
 #define MAX_INSNS      512
 
 char bpf_log_buf[BPF_LOG_BUF_SIZE];
+static bool verbose = false;
 
 struct sock_test {
        const char *descr;
@@ -325,6 +326,7 @@ static int load_sock_prog(const struct bpf_insn *prog,
                          enum bpf_attach_type attach_type)
 {
        struct bpf_load_program_attr attr;
+       int ret;
 
        memset(&attr, 0, sizeof(struct bpf_load_program_attr));
        attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
@@ -332,8 +334,13 @@ static int load_sock_prog(const struct bpf_insn *prog,
        attr.insns = prog;
        attr.insns_cnt = probe_prog_length(attr.insns);
        attr.license = "GPL";
+       attr.log_level = 2;
 
-       return bpf_load_program_xattr(&attr, bpf_log_buf, BPF_LOG_BUF_SIZE);
+       ret = bpf_load_program_xattr(&attr, bpf_log_buf, BPF_LOG_BUF_SIZE);
+       if (verbose && ret < 0)
+               fprintf(stderr, "%s\n", bpf_log_buf);
+
+       return ret;
 }
 
 static int attach_sock_prog(int cgfd, int progfd,
diff --git a/tools/testing/selftests/bpf/test_sock_fields.c b/tools/testing/selftests/bpf/test_sock_fields.c
new file mode 100644 (file)
index 0000000..9bb5836
--- /dev/null
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2019 Facebook */
+
+#include <sys/socket.h>
+#include <sys/epoll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#include "cgroup_helpers.h"
+
+enum bpf_array_idx {
+       SRV_IDX,
+       CLI_IDX,
+       __NR_BPF_ARRAY_IDX,
+};
+
+#define CHECK(condition, tag, format...) ({                            \
+       int __ret = !!(condition);                                      \
+       if (__ret) {                                                    \
+               printf("%s(%d):FAIL:%s ", __func__, __LINE__, tag);     \
+               printf(format);                                         \
+               printf("\n");                                           \
+               exit(-1);                                               \
+       }                                                               \
+})
+
+#define TEST_CGROUP "/test-bpf-sock-fields"
+#define DATA "Hello BPF!"
+#define DATA_LEN sizeof(DATA)
+
+static struct sockaddr_in6 srv_sa6, cli_sa6;
+static int linum_map_fd;
+static int addr_map_fd;
+static int tp_map_fd;
+static int sk_map_fd;
+static __u32 srv_idx = SRV_IDX;
+static __u32 cli_idx = CLI_IDX;
+
+static void init_loopback6(struct sockaddr_in6 *sa6)
+{
+       memset(sa6, 0, sizeof(*sa6));
+       sa6->sin6_family = AF_INET6;
+       sa6->sin6_addr = in6addr_loopback;
+}
+
+static void print_sk(const struct bpf_sock *sk)
+{
+       char src_ip4[24], dst_ip4[24];
+       char src_ip6[64], dst_ip6[64];
+
+       inet_ntop(AF_INET, &sk->src_ip4, src_ip4, sizeof(src_ip4));
+       inet_ntop(AF_INET6, &sk->src_ip6, src_ip6, sizeof(src_ip6));
+       inet_ntop(AF_INET, &sk->dst_ip4, dst_ip4, sizeof(dst_ip4));
+       inet_ntop(AF_INET6, &sk->dst_ip6, dst_ip6, sizeof(dst_ip6));
+
+       printf("state:%u bound_dev_if:%u family:%u type:%u protocol:%u mark:%u priority:%u "
+              "src_ip4:%x(%s) src_ip6:%x:%x:%x:%x(%s) src_port:%u "
+              "dst_ip4:%x(%s) dst_ip6:%x:%x:%x:%x(%s) dst_port:%u\n",
+              sk->state, sk->bound_dev_if, sk->family, sk->type, sk->protocol,
+              sk->mark, sk->priority,
+              sk->src_ip4, src_ip4,
+              sk->src_ip6[0], sk->src_ip6[1], sk->src_ip6[2], sk->src_ip6[3],
+              src_ip6, sk->src_port,
+              sk->dst_ip4, dst_ip4,
+              sk->dst_ip6[0], sk->dst_ip6[1], sk->dst_ip6[2], sk->dst_ip6[3],
+              dst_ip6, ntohs(sk->dst_port));
+}
+
+static void print_tp(const struct bpf_tcp_sock *tp)
+{
+       printf("snd_cwnd:%u srtt_us:%u rtt_min:%u snd_ssthresh:%u rcv_nxt:%u "
+              "snd_nxt:%u snd:una:%u mss_cache:%u ecn_flags:%u "
+              "rate_delivered:%u rate_interval_us:%u packets_out:%u "
+              "retrans_out:%u total_retrans:%u segs_in:%u data_segs_in:%u "
+              "segs_out:%u data_segs_out:%u lost_out:%u sacked_out:%u "
+              "bytes_received:%llu bytes_acked:%llu\n",
+              tp->snd_cwnd, tp->srtt_us, tp->rtt_min, tp->snd_ssthresh,
+              tp->rcv_nxt, tp->snd_nxt, tp->snd_una, tp->mss_cache,
+              tp->ecn_flags, tp->rate_delivered, tp->rate_interval_us,
+              tp->packets_out, tp->retrans_out, tp->total_retrans,
+              tp->segs_in, tp->data_segs_in, tp->segs_out,
+              tp->data_segs_out, tp->lost_out, tp->sacked_out,
+              tp->bytes_received, tp->bytes_acked);
+}
+
+static void check_result(void)
+{
+       struct bpf_tcp_sock srv_tp, cli_tp;
+       struct bpf_sock srv_sk, cli_sk;
+       __u32 linum, idx0 = 0;
+       int err;
+
+       err = bpf_map_lookup_elem(linum_map_fd, &idx0, &linum);
+       CHECK(err == -1, "bpf_map_lookup_elem(linum_map_fd)",
+             "err:%d errno:%d", err, errno);
+
+       err = bpf_map_lookup_elem(sk_map_fd, &srv_idx, &srv_sk);
+       CHECK(err == -1, "bpf_map_lookup_elem(sk_map_fd, &srv_idx)",
+             "err:%d errno:%d", err, errno);
+       err = bpf_map_lookup_elem(tp_map_fd, &srv_idx, &srv_tp);
+       CHECK(err == -1, "bpf_map_lookup_elem(tp_map_fd, &srv_idx)",
+             "err:%d errno:%d", err, errno);
+
+       err = bpf_map_lookup_elem(sk_map_fd, &cli_idx, &cli_sk);
+       CHECK(err == -1, "bpf_map_lookup_elem(sk_map_fd, &cli_idx)",
+             "err:%d errno:%d", err, errno);
+       err = bpf_map_lookup_elem(tp_map_fd, &cli_idx, &cli_tp);
+       CHECK(err == -1, "bpf_map_lookup_elem(tp_map_fd, &cli_idx)",
+             "err:%d errno:%d", err, errno);
+
+       printf("srv_sk: ");
+       print_sk(&srv_sk);
+       printf("\n");
+
+       printf("cli_sk: ");
+       print_sk(&cli_sk);
+       printf("\n");
+
+       printf("srv_tp: ");
+       print_tp(&srv_tp);
+       printf("\n");
+
+       printf("cli_tp: ");
+       print_tp(&cli_tp);
+       printf("\n");
+
+       CHECK(srv_sk.state == 10 ||
+             !srv_sk.state ||
+             srv_sk.family != AF_INET6 ||
+             srv_sk.protocol != IPPROTO_TCP ||
+             memcmp(srv_sk.src_ip6, &in6addr_loopback,
+                    sizeof(srv_sk.src_ip6)) ||
+             memcmp(srv_sk.dst_ip6, &in6addr_loopback,
+                    sizeof(srv_sk.dst_ip6)) ||
+             srv_sk.src_port != ntohs(srv_sa6.sin6_port) ||
+             srv_sk.dst_port != cli_sa6.sin6_port,
+             "Unexpected srv_sk", "Check srv_sk output. linum:%u", linum);
+
+       CHECK(cli_sk.state == 10 ||
+             !cli_sk.state ||
+             cli_sk.family != AF_INET6 ||
+             cli_sk.protocol != IPPROTO_TCP ||
+             memcmp(cli_sk.src_ip6, &in6addr_loopback,
+                    sizeof(cli_sk.src_ip6)) ||
+             memcmp(cli_sk.dst_ip6, &in6addr_loopback,
+                    sizeof(cli_sk.dst_ip6)) ||
+             cli_sk.src_port != ntohs(cli_sa6.sin6_port) ||
+             cli_sk.dst_port != srv_sa6.sin6_port,
+             "Unexpected cli_sk", "Check cli_sk output. linum:%u", linum);
+
+       CHECK(srv_tp.data_segs_out != 1 ||
+             srv_tp.data_segs_in ||
+             srv_tp.snd_cwnd != 10 ||
+             srv_tp.total_retrans ||
+             srv_tp.bytes_acked != DATA_LEN,
+             "Unexpected srv_tp", "Check srv_tp output. linum:%u", linum);
+
+       CHECK(cli_tp.data_segs_out ||
+             cli_tp.data_segs_in != 1 ||
+             cli_tp.snd_cwnd != 10 ||
+             cli_tp.total_retrans ||
+             cli_tp.bytes_received != DATA_LEN,
+             "Unexpected cli_tp", "Check cli_tp output. linum:%u", linum);
+}
+
+static void test(void)
+{
+       int listen_fd, cli_fd, accept_fd, epfd, err;
+       struct epoll_event ev;
+       socklen_t addrlen;
+
+       addrlen = sizeof(struct sockaddr_in6);
+       ev.events = EPOLLIN;
+
+       epfd = epoll_create(1);
+       CHECK(epfd == -1, "epoll_create()", "epfd:%d errno:%d", epfd, errno);
+
+       /* Prepare listen_fd */
+       listen_fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0);
+       CHECK(listen_fd == -1, "socket()", "listen_fd:%d errno:%d",
+             listen_fd, errno);
+
+       init_loopback6(&srv_sa6);
+       err = bind(listen_fd, (struct sockaddr *)&srv_sa6, sizeof(srv_sa6));
+       CHECK(err, "bind(listen_fd)", "err:%d errno:%d", err, errno);
+
+       err = getsockname(listen_fd, (struct sockaddr *)&srv_sa6, &addrlen);
+       CHECK(err, "getsockname(listen_fd)", "err:%d errno:%d", err, errno);
+
+       err = listen(listen_fd, 1);
+       CHECK(err, "listen(listen_fd)", "err:%d errno:%d", err, errno);
+
+       /* Prepare cli_fd */
+       cli_fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0);
+       CHECK(cli_fd == -1, "socket()", "cli_fd:%d errno:%d", cli_fd, errno);
+
+       init_loopback6(&cli_sa6);
+       err = bind(cli_fd, (struct sockaddr *)&cli_sa6, sizeof(cli_sa6));
+       CHECK(err, "bind(cli_fd)", "err:%d errno:%d", err, errno);
+
+       err = getsockname(cli_fd, (struct sockaddr *)&cli_sa6, &addrlen);
+       CHECK(err, "getsockname(cli_fd)", "err:%d errno:%d",
+             err, errno);
+
+       /* Update addr_map with srv_sa6 and cli_sa6 */
+       err = bpf_map_update_elem(addr_map_fd, &srv_idx, &srv_sa6, 0);
+       CHECK(err, "map_update", "err:%d errno:%d", err, errno);
+
+       err = bpf_map_update_elem(addr_map_fd, &cli_idx, &cli_sa6, 0);
+       CHECK(err, "map_update", "err:%d errno:%d", err, errno);
+
+       /* Connect from cli_sa6 to srv_sa6 */
+       err = connect(cli_fd, (struct sockaddr *)&srv_sa6, addrlen);
+       printf("srv_sa6.sin6_port:%u cli_sa6.sin6_port:%u\n\n",
+              ntohs(srv_sa6.sin6_port), ntohs(cli_sa6.sin6_port));
+       CHECK(err && errno != EINPROGRESS,
+             "connect(cli_fd)", "err:%d errno:%d", err, errno);
+
+       ev.data.fd = listen_fd;
+       err = epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev);
+       CHECK(err, "epoll_ctl(EPOLL_CTL_ADD, listen_fd)", "err:%d errno:%d",
+             err, errno);
+
+       /* Accept the connection */
+       /* Have some timeout in accept(listen_fd). Just in case. */
+       err = epoll_wait(epfd, &ev, 1, 1000);
+       CHECK(err != 1 || ev.data.fd != listen_fd,
+             "epoll_wait(listen_fd)",
+             "err:%d errno:%d ev.data.fd:%d listen_fd:%d",
+             err, errno, ev.data.fd, listen_fd);
+
+       accept_fd = accept(listen_fd, NULL, NULL);
+       CHECK(accept_fd == -1, "accept(listen_fd)", "accept_fd:%d errno:%d",
+             accept_fd, errno);
+       close(listen_fd);
+
+       /* Send some data from accept_fd to cli_fd */
+       err = send(accept_fd, DATA, DATA_LEN, 0);
+       CHECK(err != DATA_LEN, "send(accept_fd)", "err:%d errno:%d",
+             err, errno);
+
+       /* Have some timeout in recv(cli_fd). Just in case. */
+       ev.data.fd = cli_fd;
+       err = epoll_ctl(epfd, EPOLL_CTL_ADD, cli_fd, &ev);
+       CHECK(err, "epoll_ctl(EPOLL_CTL_ADD, cli_fd)", "err:%d errno:%d",
+             err, errno);
+
+       err = epoll_wait(epfd, &ev, 1, 1000);
+       CHECK(err != 1 || ev.data.fd != cli_fd,
+             "epoll_wait(cli_fd)", "err:%d errno:%d ev.data.fd:%d cli_fd:%d",
+             err, errno, ev.data.fd, cli_fd);
+
+       err = recv(cli_fd, NULL, 0, MSG_TRUNC);
+       CHECK(err, "recv(cli_fd)", "err:%d errno:%d", err, errno);
+
+       close(epfd);
+       close(accept_fd);
+       close(cli_fd);
+
+       check_result();
+}
+
+int main(int argc, char **argv)
+{
+       struct bpf_prog_load_attr attr = {
+               .file = "test_sock_fields_kern.o",
+               .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+               .expected_attach_type = BPF_CGROUP_INET_EGRESS,
+       };
+       int cgroup_fd, prog_fd, err;
+       struct bpf_object *obj;
+       struct bpf_map *map;
+
+       err = setup_cgroup_environment();
+       CHECK(err, "setup_cgroup_environment()", "err:%d errno:%d",
+             err, errno);
+
+       atexit(cleanup_cgroup_environment);
+
+       /* Create a cgroup, get fd, and join it */
+       cgroup_fd = create_and_get_cgroup(TEST_CGROUP);
+       CHECK(cgroup_fd == -1, "create_and_get_cgroup()",
+             "cgroup_fd:%d errno:%d", cgroup_fd, errno);
+
+       err = join_cgroup(TEST_CGROUP);
+       CHECK(err, "join_cgroup", "err:%d errno:%d", err, errno);
+
+       err = bpf_prog_load_xattr(&attr, &obj, &prog_fd);
+       CHECK(err, "bpf_prog_load_xattr()", "err:%d", err);
+
+       err = bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_INET_EGRESS, 0);
+       CHECK(err == -1, "bpf_prog_attach(CPF_CGROUP_INET_EGRESS)",
+             "err:%d errno%d", err, errno);
+       close(cgroup_fd);
+
+       map = bpf_object__find_map_by_name(obj, "addr_map");
+       CHECK(!map, "cannot find addr_map", "(null)");
+       addr_map_fd = bpf_map__fd(map);
+
+       map = bpf_object__find_map_by_name(obj, "sock_result_map");
+       CHECK(!map, "cannot find sock_result_map", "(null)");
+       sk_map_fd = bpf_map__fd(map);
+
+       map = bpf_object__find_map_by_name(obj, "tcp_sock_result_map");
+       CHECK(!map, "cannot find tcp_sock_result_map", "(null)");
+       tp_map_fd = bpf_map__fd(map);
+
+       map = bpf_object__find_map_by_name(obj, "linum_map");
+       CHECK(!map, "cannot find linum_map", "(null)");
+       linum_map_fd = bpf_map__fd(map);
+
+       test();
+
+       bpf_object__close(obj);
+       cleanup_cgroup_environment();
+
+       printf("PASS\n");
+
+       return 0;
+}
index c5e2242..477a9dc 100644 (file)
 #include <linux/bpf_perf_event.h>
 #include <linux/bpf.h>
 #include <linux/if_ether.h>
+#include <linux/btf.h>
 
 #include <bpf/bpf.h>
+#include <bpf/libbpf.h>
 
 #ifdef HAVE_GENHDR
 # include "autoconf.h"
@@ -49,7 +51,7 @@
 
 #define MAX_INSNS      BPF_MAXINSNS
 #define MAX_FIXUPS     8
-#define MAX_NR_MAPS    13
+#define MAX_NR_MAPS    14
 #define MAX_TEST_RUNS  8
 #define POINTER_VALUE  0xcafe4all
 #define TEST_DATA_LEN  64
@@ -59,6 +61,7 @@
 
 #define UNPRIV_SYSCTL "kernel/unprivileged_bpf_disabled"
 static bool unpriv_disabled = false;
+static int skips;
 
 struct bpf_test {
        const char *descr;
@@ -76,6 +79,7 @@ struct bpf_test {
        int fixup_map_in_map[MAX_FIXUPS];
        int fixup_cgroup_storage[MAX_FIXUPS];
        int fixup_percpu_cgroup_storage[MAX_FIXUPS];
+       int fixup_map_spin_lock[MAX_FIXUPS];
        const char *errstr;
        const char *errstr_unpriv;
        uint32_t retval, retval_unpriv, insn_processed;
@@ -263,6 +267,16 @@ static int probe_filter_length(const struct bpf_insn *fp)
        return len + 1;
 }
 
+static bool skip_unsupported_map(enum bpf_map_type map_type)
+{
+       if (!bpf_probe_map_type(map_type, 0)) {
+               printf("SKIP (unsupported map type %d)\n", map_type);
+               skips++;
+               return true;
+       }
+       return false;
+}
+
 static int create_map(uint32_t type, uint32_t size_key,
                      uint32_t size_value, uint32_t max_elem)
 {
@@ -270,8 +284,11 @@ static int create_map(uint32_t type, uint32_t size_key,
 
        fd = bpf_create_map(type, size_key, size_value, max_elem,
                            type == BPF_MAP_TYPE_HASH ? BPF_F_NO_PREALLOC : 0);
-       if (fd < 0)
+       if (fd < 0) {
+               if (skip_unsupported_map(type))
+                       return -1;
                printf("Failed to create hash map '%s'!\n", strerror(errno));
+       }
 
        return fd;
 }
@@ -321,6 +338,8 @@ static int create_prog_array(enum bpf_prog_type prog_type, uint32_t max_elem,
        mfd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int),
                             sizeof(int), max_elem, 0);
        if (mfd < 0) {
+               if (skip_unsupported_map(BPF_MAP_TYPE_PROG_ARRAY))
+                       return -1;
                printf("Failed to create prog array '%s'!\n", strerror(errno));
                return -1;
        }
@@ -351,15 +370,20 @@ static int create_map_in_map(void)
        inner_map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int),
                                      sizeof(int), 1, 0);
        if (inner_map_fd < 0) {
+               if (skip_unsupported_map(BPF_MAP_TYPE_ARRAY))
+                       return -1;
                printf("Failed to create array '%s'!\n", strerror(errno));
                return inner_map_fd;
        }
 
        outer_map_fd = bpf_create_map_in_map(BPF_MAP_TYPE_ARRAY_OF_MAPS, NULL,
                                             sizeof(int), inner_map_fd, 1, 0);
-       if (outer_map_fd < 0)
+       if (outer_map_fd < 0) {
+               if (skip_unsupported_map(BPF_MAP_TYPE_ARRAY_OF_MAPS))
+                       return -1;
                printf("Failed to create array of maps '%s'!\n",
                       strerror(errno));
+       }
 
        close(inner_map_fd);
 
@@ -374,10 +398,105 @@ static int create_cgroup_storage(bool percpu)
 
        fd = bpf_create_map(type, sizeof(struct bpf_cgroup_storage_key),
                            TEST_DATA_LEN, 0, 0);
-       if (fd < 0)
+       if (fd < 0) {
+               if (skip_unsupported_map(type))
+                       return -1;
                printf("Failed to create cgroup storage '%s'!\n",
                       strerror(errno));
+       }
+
+       return fd;
+}
+
+#define BTF_INFO_ENC(kind, kind_flag, vlen) \
+       ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
+#define BTF_TYPE_ENC(name, info, size_or_type) \
+       (name), (info), (size_or_type)
+#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \
+       ((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
+#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \
+       BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \
+       BTF_INT_ENC(encoding, bits_offset, bits)
+#define BTF_MEMBER_ENC(name, type, bits_offset) \
+       (name), (type), (bits_offset)
+
+struct btf_raw_data {
+       __u32 raw_types[64];
+       const char *str_sec;
+       __u32 str_sec_size;
+};
+
+/* struct bpf_spin_lock {
+ *   int val;
+ * };
+ * struct val {
+ *   int cnt;
+ *   struct bpf_spin_lock l;
+ * };
+ */
+static const char btf_str_sec[] = "\0bpf_spin_lock\0val\0cnt\0l";
+static __u32 btf_raw_types[] = {
+       /* int */
+       BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+       /* struct bpf_spin_lock */                      /* [2] */
+       BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 1), 4),
+       BTF_MEMBER_ENC(15, 1, 0), /* int val; */
+       /* struct val */                                /* [3] */
+       BTF_TYPE_ENC(15, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 2), 8),
+       BTF_MEMBER_ENC(19, 1, 0), /* int cnt; */
+       BTF_MEMBER_ENC(23, 2, 32),/* struct bpf_spin_lock l; */
+};
+
+static int load_btf(void)
+{
+       struct btf_header hdr = {
+               .magic = BTF_MAGIC,
+               .version = BTF_VERSION,
+               .hdr_len = sizeof(struct btf_header),
+               .type_len = sizeof(btf_raw_types),
+               .str_off = sizeof(btf_raw_types),
+               .str_len = sizeof(btf_str_sec),
+       };
+       void *ptr, *raw_btf;
+       int btf_fd;
+
+       ptr = raw_btf = malloc(sizeof(hdr) + sizeof(btf_raw_types) +
+                              sizeof(btf_str_sec));
+
+       memcpy(ptr, &hdr, sizeof(hdr));
+       ptr += sizeof(hdr);
+       memcpy(ptr, btf_raw_types, hdr.type_len);
+       ptr += hdr.type_len;
+       memcpy(ptr, btf_str_sec, hdr.str_len);
+       ptr += hdr.str_len;
+
+       btf_fd = bpf_load_btf(raw_btf, ptr - raw_btf, 0, 0, 0);
+       free(raw_btf);
+       if (btf_fd < 0)
+               return -1;
+       return btf_fd;
+}
+
+static int create_map_spin_lock(void)
+{
+       struct bpf_create_map_attr attr = {
+               .name = "test_map",
+               .map_type = BPF_MAP_TYPE_ARRAY,
+               .key_size = 4,
+               .value_size = 8,
+               .max_entries = 1,
+               .btf_key_type_id = 1,
+               .btf_value_type_id = 3,
+       };
+       int fd, btf_fd;
 
+       btf_fd = load_btf();
+       if (btf_fd < 0)
+               return -1;
+       attr.btf_fd = btf_fd;
+       fd = bpf_create_map_xattr(&attr);
+       if (fd < 0)
+               printf("Failed to create map with spin_lock\n");
        return fd;
 }
 
@@ -399,6 +518,7 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
        int *fixup_map_in_map = test->fixup_map_in_map;
        int *fixup_cgroup_storage = test->fixup_cgroup_storage;
        int *fixup_percpu_cgroup_storage = test->fixup_percpu_cgroup_storage;
+       int *fixup_map_spin_lock = test->fixup_map_spin_lock;
 
        if (test->fill_helper)
                test->fill_helper(test);
@@ -515,6 +635,13 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
                        fixup_map_stacktrace++;
                } while (*fixup_map_stacktrace);
        }
+       if (*fixup_map_spin_lock) {
+               map_fds[13] = create_map_spin_lock();
+               do {
+                       prog[*fixup_map_spin_lock].imm = map_fds[13];
+                       fixup_map_spin_lock++;
+               } while (*fixup_map_spin_lock);
+       }
 }
 
 static int set_admin(bool admin)
@@ -580,6 +707,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
        int run_errs, run_successes;
        int map_fds[MAX_NR_MAPS];
        const char *expected_err;
+       int fixup_skips;
        __u32 pflags;
        int i, err;
 
@@ -588,7 +716,13 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
 
        if (!prog_type)
                prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
+       fixup_skips = skips;
        do_test_fixup(test, prog_type, prog, map_fds);
+       /* If there were some map skips during fixup due to missing bpf
+        * features, skip this test.
+        */
+       if (fixup_skips != skips)
+               return;
        prog_len = probe_filter_length(prog);
 
        pflags = 0;
@@ -598,6 +732,11 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
                pflags |= BPF_F_ANY_ALIGNMENT;
        fd_prog = bpf_verify_program(prog_type, prog, prog_len, pflags,
                                     "GPL", 0, bpf_vlog, sizeof(bpf_vlog), 1);
+       if (fd_prog < 0 && !bpf_probe_prog_type(prog_type, 0)) {
+               printf("SKIP (unsupported program type %d)\n", prog_type);
+               skips++;
+               goto close_fds;
+       }
 
        expected_ret = unpriv && test->result_unpriv != UNDEF ?
                       test->result_unpriv : test->result;
@@ -751,7 +890,7 @@ static bool test_as_unpriv(struct bpf_test *test)
 
 static int do_test(bool unpriv, unsigned int from, unsigned int to)
 {
-       int i, passes = 0, errors = 0, skips = 0;
+       int i, passes = 0, errors = 0;
 
        for (i = from; i < to; i++) {
                struct bpf_test *test = &tests[i];
index b019577..c6c6922 100644 (file)
        .errstr = "invalid bpf_context access",
        .result = REJECT,
        .prog_type = BPF_PROG_TYPE_SK_MSG,
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "invalid read past end of SK_MSG",
index 881f1c7..c660deb 100644 (file)
        },
        .errstr = "invalid bpf_context access",
        .result = REJECT,
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "check skb->hash half load not permitted, unaligned 3",
index ceb39ff..f0961c5 100644 (file)
@@ -27,6 +27,7 @@
                  .data64 = { 1ULL << 63 | 1, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jset32: BPF_X",
@@ -58,6 +59,7 @@
                  .data64 = { 1ULL << 63 | 1, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jset32: min/max deduction",
@@ -93,6 +95,7 @@
                  .data64 = { -1, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jeq32: BPF_X",
                  .data64 = { 1ULL << 63 | 1, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jeq32: min/max deduction",
                  .data64 = { -1, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jne32: BPF_X",
                  .data64 = { 1ULL << 63 | 2, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jne32: min/max deduction",
                  .data64 = { 0, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jge32: BPF_X",
                  .data64 = { (UINT_MAX - 1) | 2ULL << 32, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jge32: min/max deduction",
                  .data64 = { 0, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jgt32: BPF_X",
                  .data64 = { (UINT_MAX - 1) | 2ULL << 32, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jgt32: min/max deduction",
                  .data64 = { INT_MAX, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jle32: BPF_X",
                  .data64 = { UINT_MAX, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jle32: min/max deduction",
                  .data64 = { INT_MAX - 1, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jlt32: BPF_X",
                  .data64 = { (INT_MAX - 1) | 3ULL << 32, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jlt32: min/max deduction",
                  .data64 = { -2, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jsge32: BPF_X",
                  .data64 = { -2, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jsge32: min/max deduction",
                  .data64 = { 1, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jsgt32: BPF_X",
                  .data64 = { 0x7fffffff, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jsgt32: min/max deduction",
                  .data64 = { 1, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jsle32: BPF_X",
                  .data64 = { 0x7fffffff | 2ULL << 32, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jsle32: min/max deduction",
                  .data64 = { 1, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jslt32: BPF_X",
                  .data64 = { 0x7fffffff | 2ULL << 32, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jslt32: min/max deduction",
index 7e14037..8dcd4e0 100644 (file)
@@ -53,6 +53,7 @@
                  .data64 = { ~0ULL, }
                },
        },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jset: sign-extend",
@@ -70,6 +71,7 @@
        .result = ACCEPT,
        .retval = 2,
        .data = { 1, 0, 0, 0, 0, 0, 0, 1, },
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "jset: known const compare",
index dc2cc82..3ed3593 100644 (file)
        BPF_EXIT_INSN(),
        },
        .prog_type = BPF_PROG_TYPE_SCHED_CLS,
-       .errstr = "cannot write into socket",
+       .errstr = "cannot write into sock",
        .result = REJECT,
 },
 {
        BPF_EXIT_INSN(),
        },
        .prog_type = BPF_PROG_TYPE_SCHED_CLS,
-       .errstr = "invalid bpf_sock access off=0 size=8",
+       .errstr = "invalid sock access off=0 size=8",
        .result = REJECT,
 },
 {
diff --git a/tools/testing/selftests/bpf/verifier/sock.c b/tools/testing/selftests/bpf/verifier/sock.c
new file mode 100644 (file)
index 0000000..0ddfdf7
--- /dev/null
@@ -0,0 +1,384 @@
+{
+       "skb->sk: no NULL check",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = REJECT,
+       .errstr = "invalid mem access 'sock_common_or_null'",
+},
+{
+       "skb->sk: sk->family [non fullsock field]",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, offsetof(struct bpf_sock, family)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = ACCEPT,
+},
+{
+       "skb->sk: sk->type [fullsock field]",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, offsetof(struct bpf_sock, type)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = REJECT,
+       .errstr = "invalid sock_common access",
+},
+{
+       "bpf_sk_fullsock(skb->sk): no !skb->sk check",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = REJECT,
+       .errstr = "type=sock_common_or_null expected=sock_common",
+},
+{
+       "sk_fullsock(skb->sk): no NULL check on ret",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, type)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = REJECT,
+       .errstr = "invalid mem access 'sock_or_null'",
+},
+{
+       "sk_fullsock(skb->sk): sk->type [fullsock field]",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, type)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = ACCEPT,
+},
+{
+       "sk_fullsock(skb->sk): sk->family [non fullsock field]",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, family)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = ACCEPT,
+},
+{
+       "sk_fullsock(skb->sk): sk->state [narrow load]",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, state)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = ACCEPT,
+},
+{
+       "sk_fullsock(skb->sk): sk->dst_port [narrow load]",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, dst_port)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = ACCEPT,
+},
+{
+       "sk_fullsock(skb->sk): sk->dst_port [load 2nd byte]",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, dst_port) + 1),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = REJECT,
+       .errstr = "invalid sock access",
+},
+{
+       "sk_fullsock(skb->sk): sk->dst_ip6 [load 2nd byte]",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, dst_ip6[0]) + 1),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = ACCEPT,
+},
+{
+       "sk_fullsock(skb->sk): sk->type [narrow load]",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, type)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = ACCEPT,
+},
+{
+       "sk_fullsock(skb->sk): sk->protocol [narrow load]",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, protocol)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = ACCEPT,
+},
+{
+       "sk_fullsock(skb->sk): beyond last field",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetofend(struct bpf_sock, state)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = REJECT,
+       .errstr = "invalid sock access",
+},
+{
+       "bpf_tcp_sock(skb->sk): no !skb->sk check",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_EMIT_CALL(BPF_FUNC_tcp_sock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = REJECT,
+       .errstr = "type=sock_common_or_null expected=sock_common",
+},
+{
+       "bpf_tcp_sock(skb->sk): no NULL check on ret",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_tcp_sock),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_tcp_sock, snd_cwnd)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = REJECT,
+       .errstr = "invalid mem access 'tcp_sock_or_null'",
+},
+{
+       "bpf_tcp_sock(skb->sk): tp->snd_cwnd",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_tcp_sock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_tcp_sock, snd_cwnd)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = ACCEPT,
+},
+{
+       "bpf_tcp_sock(skb->sk): tp->bytes_acked",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_tcp_sock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_tcp_sock, bytes_acked)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = ACCEPT,
+},
+{
+       "bpf_tcp_sock(skb->sk): beyond last field",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_tcp_sock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, offsetofend(struct bpf_tcp_sock, bytes_acked)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = REJECT,
+       .errstr = "invalid tcp_sock access",
+},
+{
+       "bpf_tcp_sock(bpf_sk_fullsock(skb->sk)): tp->snd_cwnd",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_EMIT_CALL(BPF_FUNC_tcp_sock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_tcp_sock, snd_cwnd)),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .result = ACCEPT,
+},
+{
+       "bpf_sk_release(skb->sk)",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
+       BPF_EMIT_CALL(BPF_FUNC_sk_release),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+       .result = REJECT,
+       .errstr = "type=sock_common expected=sock",
+},
+{
+       "bpf_sk_release(bpf_sk_fullsock(skb->sk))",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_EMIT_CALL(BPF_FUNC_sk_release),
+       BPF_MOV64_IMM(BPF_REG_0, 1),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+       .result = REJECT,
+       .errstr = "reference has not been acquired before",
+},
+{
+       "bpf_sk_release(bpf_tcp_sock(skb->sk))",
+       .insns = {
+       BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       BPF_EMIT_CALL(BPF_FUNC_tcp_sock),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_EMIT_CALL(BPF_FUNC_sk_release),
+       BPF_MOV64_IMM(BPF_REG_0, 1),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+       .result = REJECT,
+       .errstr = "type=tcp_sock expected=sock",
+},
index d58db72..45d43bf 100644 (file)
@@ -46,6 +46,7 @@
        .errstr_unpriv = "attempt to corrupt spilled",
        .errstr = "R0 invalid mem access 'inv",
        .result = REJECT,
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "check corrupted spill/fill, LSB",
diff --git a/tools/testing/selftests/bpf/verifier/spin_lock.c b/tools/testing/selftests/bpf/verifier/spin_lock.c
new file mode 100644 (file)
index 0000000..781621f
--- /dev/null
@@ -0,0 +1,333 @@
+{
+       "spin_lock: test1 success",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_spin_lock = { 3 },
+       .result = ACCEPT,
+       .result_unpriv = REJECT,
+       .errstr_unpriv = "",
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+},
+{
+       "spin_lock: test2 direct ld/st",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_spin_lock = { 3 },
+       .result = REJECT,
+       .errstr = "cannot be accessed directly",
+       .result_unpriv = REJECT,
+       .errstr_unpriv = "",
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+},
+{
+       "spin_lock: test3 direct ld/st",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 1),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_spin_lock = { 3 },
+       .result = REJECT,
+       .errstr = "cannot be accessed directly",
+       .result_unpriv = REJECT,
+       .errstr_unpriv = "",
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
+},
+{
+       "spin_lock: test4 direct ld/st",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_6, 3),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_spin_lock = { 3 },
+       .result = REJECT,
+       .errstr = "cannot be accessed directly",
+       .result_unpriv = REJECT,
+       .errstr_unpriv = "",
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
+},
+{
+       "spin_lock: test5 call within a locked region",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_spin_lock = { 3 },
+       .result = REJECT,
+       .errstr = "calls are not allowed",
+       .result_unpriv = REJECT,
+       .errstr_unpriv = "",
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+},
+{
+       "spin_lock: test6 missing unlock",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_spin_lock = { 3 },
+       .result = REJECT,
+       .errstr = "unlock is missing",
+       .result_unpriv = REJECT,
+       .errstr_unpriv = "",
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+},
+{
+       "spin_lock: test7 unlock without lock",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 1),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_spin_lock = { 3 },
+       .result = REJECT,
+       .errstr = "without taking a lock",
+       .result_unpriv = REJECT,
+       .errstr_unpriv = "",
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+},
+{
+       "spin_lock: test8 double lock",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_spin_lock = { 3 },
+       .result = REJECT,
+       .errstr = "calls are not allowed",
+       .result_unpriv = REJECT,
+       .errstr_unpriv = "",
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+},
+{
+       "spin_lock: test9 different lock",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_spin_lock = { 3, 11 },
+       .result = REJECT,
+       .errstr = "unlock of different lock",
+       .result_unpriv = REJECT,
+       .errstr_unpriv = "",
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+},
+{
+       "spin_lock: test10 lock in subprog without unlock",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 5),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
+       BPF_MOV64_IMM(BPF_REG_0, 1),
+       BPF_EXIT_INSN(),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_spin_lock = { 3 },
+       .result = REJECT,
+       .errstr = "unlock is missing",
+       .result_unpriv = REJECT,
+       .errstr_unpriv = "",
+       .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+},
+{
+       "spin_lock: test11 ld_abs under lock",
+       .insns = {
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1,
+                     0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_lock),
+       BPF_LD_ABS(BPF_B, 0),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 4),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_spin_unlock),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_spin_lock = { 4 },
+       .result = REJECT,
+       .errstr = "inside bpf_spin_lock",
+       .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+},
index dca58cf..dbaf5be 100644 (file)
@@ -76,6 +76,7 @@
        .errstr_unpriv = "unknown func bpf_trace_printk#6",
        .result_unpriv = REJECT,
        .result = ACCEPT,
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
 },
 {
        "unpriv: pass pointer to helper function",
        },
        .result = REJECT,
        //.errstr = "same insn cannot be used with different pointers",
-       .errstr = "cannot write into socket",
+       .errstr = "cannot write into sock",
        .prog_type = BPF_PROG_TYPE_SCHED_CLS,
 },
 {
index 9ab5ace..4b721a7 100644 (file)
        .fixup_map_array_48b = { 3 },
        .result = ACCEPT,
        .retval = 0xabcdef12,
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "map access: unknown scalar += value_ptr, 3",
        .result_unpriv = REJECT,
        .errstr_unpriv = "R0 pointer arithmetic of map value goes out of range",
        .retval = 0xabcdef12,
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "map access: unknown scalar += value_ptr, 4",
        .result = REJECT,
        .errstr = "R1 max value is outside of the array range",
        .errstr_unpriv = "R1 pointer arithmetic of map value goes out of range",
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "map access: value_ptr += unknown scalar, 1",
        .fixup_map_array_48b = { 3 },
        .result = ACCEPT,
        .retval = 0xabcdef12,
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
 {
        "map access: value_ptr += unknown scalar, 3",
index bab13dd..0d26b5e 100755 (executable)
@@ -37,6 +37,10 @@ prerequisite()
                exit $ksft_skip
        fi
 
+       present_cpus=`cat $SYSFS/devices/system/cpu/present`
+       present_max=${present_cpus##*-}
+       echo "present_cpus = $present_cpus present_max = $present_max"
+
        echo -e "\t Cpus in online state: $online_cpus"
 
        offline_cpus=`cat $SYSFS/devices/system/cpu/offline`
@@ -151,6 +155,8 @@ online_cpus=0
 online_max=0
 offline_cpus=0
 offline_max=0
+present_cpus=0
+present_max=0
 
 while getopts e:ahp: opt; do
        case $opt in
@@ -190,9 +196,10 @@ if [ $allcpus -eq 0 ]; then
        online_cpu_expect_success $online_max
 
        if [[ $offline_cpus -gt 0 ]]; then
-               echo -e "\t offline to online to offline: cpu $offline_max"
-               online_cpu_expect_success $offline_max
-               offline_cpu_expect_success $offline_max
+               echo -e "\t offline to online to offline: cpu $present_max"
+               online_cpu_expect_success $present_max
+               offline_cpu_expect_success $present_max
+               online_cpu $present_max
        fi
        exit 0
 else
diff --git a/tools/testing/selftests/drivers/net/mlxsw/blackhole_routes.sh b/tools/testing/selftests/drivers/net/mlxsw/blackhole_routes.sh
new file mode 100755 (executable)
index 0000000..5ba5bef
--- /dev/null
@@ -0,0 +1,200 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test that blackhole routes are marked as offloaded and that packets hitting
+# them are dropped by the ASIC and not by the kernel.
+#
+# +---------------------------------+
+# | H1 (vrf)                        |
+# |    + $h1                        |
+# |    | 192.0.2.1/24               |
+# |    | 2001:db8:1::1/64           |
+# |    |                            |
+# |    |  default via 192.0.2.2     |
+# |    |  default via 2001:db8:1::2 |
+# +----|----------------------------+
+#      |
+# +----|----------------------------------------------------------------------+
+# | SW |                                                                      |
+# |    + $rp1                                                                 |
+# |        192.0.2.2/24                                                       |
+# |        2001:db8:1::2/64                                                   |
+# |                                                                           |
+# |        2001:db8:2::2/64                                                   |
+# |        198.51.100.2/24                                                    |
+# |    + $rp2                                                                 |
+# |    |                                                                      |
+# +----|----------------------------------------------------------------------+
+#      |
+# +----|----------------------------+
+# |    |  default via 198.51.100.2  |
+# |    |  default via 2001:db8:2::2 |
+# |    |                            |
+# |    | 2001:db8:2::1/64           |
+# |    | 198.51.100.1/24            |
+# |    + $h2                        |
+# | H2 (vrf)                        |
+# +---------------------------------+
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="
+       ping_ipv4
+       ping_ipv6
+       blackhole_ipv4
+       blackhole_ipv6
+"
+NUM_NETIFS=4
+source $lib_dir/tc_common.sh
+source $lib_dir/lib.sh
+
+h1_create()
+{
+       simple_if_init $h1 192.0.2.1/24 2001:db8:1::1/64
+
+       ip -4 route add default vrf v$h1 nexthop via 192.0.2.2
+       ip -6 route add default vrf v$h1 nexthop via 2001:db8:1::2
+}
+
+h1_destroy()
+{
+       ip -6 route del default vrf v$h1 nexthop via 2001:db8:1::2
+       ip -4 route del default vrf v$h1 nexthop via 192.0.2.2
+
+       simple_if_fini $h1 192.0.2.1/24 2001:db8:1::1/64
+}
+
+h2_create()
+{
+       simple_if_init $h2 198.51.100.1/24 2001:db8:2::1/64
+
+       ip -4 route add default vrf v$h2 nexthop via 198.51.100.2
+       ip -6 route add default vrf v$h2 nexthop via 2001:db8:2::2
+}
+
+h2_destroy()
+{
+       ip -6 route del default vrf v$h2 nexthop via 2001:db8:2::2
+       ip -4 route del default vrf v$h2 nexthop via 198.51.100.2
+
+       simple_if_fini $h2 198.51.100.1/24 2001:db8:2::1/64
+}
+
+router_create()
+{
+       ip link set dev $rp1 up
+       ip link set dev $rp2 up
+
+       tc qdisc add dev $rp1 clsact
+
+       __addr_add_del $rp1 add 192.0.2.2/24 2001:db8:1::2/64
+       __addr_add_del $rp2 add 198.51.100.2/24 2001:db8:2::2/64
+}
+
+router_destroy()
+{
+       __addr_add_del $rp2 del 198.51.100.2/24 2001:db8:2::2/64
+       __addr_add_del $rp1 del 192.0.2.2/24 2001:db8:1::2/64
+
+       tc qdisc del dev $rp1 clsact
+
+       ip link set dev $rp2 down
+       ip link set dev $rp1 down
+}
+
+ping_ipv4()
+{
+       ping_test $h1 198.51.100.1 ": h1->h2"
+}
+
+ping_ipv6()
+{
+       ping6_test $h1 2001:db8:2::1 ": h1->h2"
+}
+
+blackhole_ipv4()
+{
+       # Transmit packets from H1 to H2 and make sure they are dropped by the
+       # ASIC and not by the kernel
+       RET=0
+
+       ip -4 route add blackhole 198.51.100.0/30
+       tc filter add dev $rp1 ingress protocol ip pref 1 handle 101 flower \
+               skip_hw dst_ip 198.51.100.1 src_ip 192.0.2.1 ip_proto icmp \
+               action pass
+
+       ip -4 route show 198.51.100.0/30 | grep -q offload
+       check_err $? "route not marked as offloaded when should"
+
+       ping_do $h1 198.51.100.1
+       check_fail $? "ping passed when should not"
+
+       tc_check_packets "dev $rp1 ingress" 101 0
+       check_err $? "packets trapped and not dropped by ASIC"
+
+       log_test "IPv4 blackhole route"
+
+       tc filter del dev $rp1 ingress protocol ip pref 1 handle 101 flower
+       ip -4 route del blackhole 198.51.100.0/30
+}
+
+blackhole_ipv6()
+{
+       RET=0
+
+       ip -6 route add blackhole 2001:db8:2::/120
+       tc filter add dev $rp1 ingress protocol ipv6 pref 1 handle 101 flower \
+               skip_hw dst_ip 2001:db8:2::1 src_ip 2001:db8:1::1 \
+               ip_proto icmpv6 action pass
+
+       ip -6 route show 2001:db8:2::/120 | grep -q offload
+       check_err $? "route not marked as offloaded when should"
+
+       ping6_do $h1 2001:db8:2::1
+       check_fail $? "ping passed when should not"
+
+       tc_check_packets "dev $rp1 ingress" 101 0
+       check_err $? "packets trapped and not dropped by ASIC"
+
+       log_test "IPv6 blackhole route"
+
+       tc filter del dev $rp1 ingress protocol ipv6 pref 1 handle 101 flower
+       ip -6 route del blackhole 2001:db8:2::/120
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       rp1=${NETIFS[p2]}
+
+       rp2=${NETIFS[p3]}
+       h2=${NETIFS[p4]}
+
+       vrf_prepare
+       forwarding_enable
+
+       h1_create
+       h2_create
+       router_create
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       router_destroy
+       h2_destroy
+       h1_destroy
+
+       forwarding_restore
+       vrf_cleanup
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
index 1ca631d..40f16f2 100755 (executable)
@@ -148,9 +148,10 @@ dscp_ping_test()
        eval "t0s=($(dscp_fetch_stats $dev_10 10)
                   $(dscp_fetch_stats $dev_20 20))"
 
+       local ping_timeout=$((PING_TIMEOUT * 5))
        ip vrf exec $vrf_name \
           ${PING} -Q $dscp_10 ${sip:+-I $sip} $dip \
-                  -c 10 -i 0.1 -w 2 &> /dev/null
+                  -c 10 -i 0.5 -w $ping_timeout &> /dev/null
 
        local -A t1s
        eval "t1s=($(dscp_fetch_stats $dev_10 10)
index 281d907..9faf02e 100755 (executable)
@@ -169,9 +169,10 @@ dscp_ping_test()
        eval "local -A dev1_t0s=($(dscp_fetch_stats $dev1 0))"
        eval "local -A dev2_t0s=($(dscp_fetch_stats $dev2 0))"
 
+       local ping_timeout=$((PING_TIMEOUT * 5))
        ip vrf exec $vrf_name \
           ${PING} -Q $dscp ${sip:+-I $sip} $dip \
-                  -c 10 -i 0.1 -w 2 &> /dev/null
+                  -c 10 -i 0.5 -w $ping_timeout &> /dev/null
 
        eval "local -A dev1_t1s=($(dscp_fetch_stats $dev1 0))"
        eval "local -A dev2_t1s=($(dscp_fetch_stats $dev2 0))"
index b41d625..4d5b880 100755 (executable)
@@ -9,10 +9,11 @@ lib_dir=$(dirname $0)/../../../../net/forwarding
 
 ALL_TESTS="single_mask_test identical_filters_test two_masks_test \
        multiple_masks_test ctcam_edge_cases_test delta_simple_test \
+       delta_two_masks_one_key_test delta_simple_rehash_test \
        bloom_simple_test bloom_complex_test bloom_delta_test"
 NUM_NETIFS=2
 source $lib_dir/tc_common.sh
-source $lib_dir/lib.sh
+source $lib_dir/devlink_lib.sh
 
 tcflags="skip_hw"
 
@@ -38,6 +39,55 @@ h2_destroy()
        simple_if_fini $h2 192.0.2.2/24 198.51.100.2/24
 }
 
+tp_record()
+{
+       local tracepoint=$1
+       local cmd=$2
+
+       perf record -q -e $tracepoint $cmd
+       return $?
+}
+
+tp_record_all()
+{
+       local tracepoint=$1
+       local seconds=$2
+
+       perf record -a -q -e $tracepoint sleep $seconds
+       return $?
+}
+
+__tp_hit_count()
+{
+       local tracepoint=$1
+
+       local perf_output=`perf script -F trace:event,trace`
+       return `echo $perf_output | grep "$tracepoint:" | wc -l`
+}
+
+tp_check_hits()
+{
+       local tracepoint=$1
+       local count=$2
+
+       __tp_hit_count $tracepoint
+       if [[ "$?" -ne "$count" ]]; then
+               return 1
+       fi
+       return 0
+}
+
+tp_check_hits_any()
+{
+       local tracepoint=$1
+
+       __tp_hit_count $tracepoint
+       if [[ "$?" -eq "0" ]]; then
+               return 1
+       fi
+       return 0
+}
+
 single_mask_test()
 {
        # When only a single mask is required, the device uses the master
@@ -182,20 +232,38 @@ multiple_masks_test()
        # spillage is performed correctly and that the right filter is
        # matched
 
+       if [[ "$tcflags" != "skip_sw" ]]; then
+               return 0;
+       fi
+
        local index
 
        RET=0
 
        NUM_MASKS=32
+       NUM_ERPS=16
        BASE_INDEX=100
 
        for i in $(eval echo {1..$NUM_MASKS}); do
                index=$((BASE_INDEX - i))
 
-               tc filter add dev $h2 ingress protocol ip pref $index \
-                       handle $index \
-                       flower $tcflags dst_ip 192.0.2.2/${i} src_ip 192.0.2.1 \
-                       action drop
+               if ((i > NUM_ERPS)); then
+                       exp_hits=1
+                       err_msg="$i filters - C-TCAM spill did not happen when it was expected"
+               else
+                       exp_hits=0
+                       err_msg="$i filters - C-TCAM spill happened when it should not"
+               fi
+
+               tp_record "mlxsw:mlxsw_sp_acl_atcam_entry_add_ctcam_spill" \
+                       "tc filter add dev $h2 ingress protocol ip pref $index \
+                               handle $index \
+                               flower $tcflags \
+                               dst_ip 192.0.2.2/${i} src_ip 192.0.2.1/${i} \
+                               action drop"
+               tp_check_hits "mlxsw:mlxsw_sp_acl_atcam_entry_add_ctcam_spill" \
+                               $exp_hits
+               check_err $? "$err_msg"
 
                $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 \
                        -B 192.0.2.2 -t ip -q
@@ -325,28 +393,6 @@ ctcam_edge_cases_test()
        ctcam_no_atcam_masks_test
 }
 
-tp_record()
-{
-       local tracepoint=$1
-       local cmd=$2
-
-       perf record -q -e $tracepoint $cmd
-       return $?
-}
-
-tp_check_hits()
-{
-       local tracepoint=$1
-       local count=$2
-
-       perf_output=`perf script -F trace:event,trace`
-       hits=`echo $perf_output | grep "$tracepoint:" | wc -l`
-       if [[ "$count" -ne "$hits" ]]; then
-               return 1
-       fi
-       return 0
-}
-
 delta_simple_test()
 {
        # The first filter will create eRP, the second filter will fit into
@@ -405,6 +451,120 @@ delta_simple_test()
        log_test "delta simple test ($tcflags)"
 }
 
+delta_two_masks_one_key_test()
+{
+       # If 2 keys are the same and only differ in mask in a way that
+       # they belong under the same ERP (second is delta of the first),
+       # there should be no C-TCAM spill.
+
+       RET=0
+
+       if [[ "$tcflags" != "skip_sw" ]]; then
+               return 0;
+       fi
+
+       tp_record "mlxsw:*" "tc filter add dev $h2 ingress protocol ip \
+                  pref 1 handle 101 flower $tcflags dst_ip 192.0.2.0/24 \
+                  action drop"
+       tp_check_hits "mlxsw:mlxsw_sp_acl_atcam_entry_add_ctcam_spill" 0
+       check_err $? "incorrect C-TCAM spill while inserting the first rule"
+
+       tp_record "mlxsw:*" "tc filter add dev $h2 ingress protocol ip \
+                  pref 2 handle 102 flower $tcflags dst_ip 192.0.2.2 \
+                  action drop"
+       tp_check_hits "mlxsw:mlxsw_sp_acl_atcam_entry_add_ctcam_spill" 0
+       check_err $? "incorrect C-TCAM spill while inserting the second rule"
+
+       $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+               -t ip -q
+
+       tc_check_packets "dev $h2 ingress" 101 1
+       check_err $? "Did not match on correct filter"
+
+       tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
+
+       $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+               -t ip -q
+
+       tc_check_packets "dev $h2 ingress" 102 1
+       check_err $? "Did not match on correct filter"
+
+       tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
+
+       log_test "delta two masks one key test ($tcflags)"
+}
+
+delta_simple_rehash_test()
+{
+       RET=0
+
+       if [[ "$tcflags" != "skip_sw" ]]; then
+               return 0;
+       fi
+
+       devlink dev param set $DEVLINK_DEV \
+               name acl_region_rehash_interval cmode runtime value 0
+       check_err $? "Failed to set ACL region rehash interval"
+
+       tp_record_all mlxsw:mlxsw_sp_acl_tcam_vregion_rehash 7
+       tp_check_hits_any mlxsw:mlxsw_sp_acl_tcam_vregion_rehash
+       check_fail $? "Rehash trace was hit even when rehash should be disabled"
+
+       devlink dev param set $DEVLINK_DEV \
+               name acl_region_rehash_interval cmode runtime value 3000
+       check_err $? "Failed to set ACL region rehash interval"
+
+       sleep 1
+
+       tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
+               $tcflags dst_ip 192.0.1.0/25 action drop
+       tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
+               $tcflags dst_ip 192.0.2.2 action drop
+       tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
+               $tcflags dst_ip 192.0.3.0/24 action drop
+
+       $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+               -t ip -q
+
+       tc_check_packets "dev $h2 ingress" 101 1
+       check_fail $? "Matched a wrong filter"
+
+       tc_check_packets "dev $h2 ingress" 103 1
+       check_fail $? "Matched a wrong filter"
+
+       tc_check_packets "dev $h2 ingress" 102 1
+       check_err $? "Did not match on correct filter"
+
+       tp_record_all mlxsw:* 3
+       tp_check_hits_any mlxsw:mlxsw_sp_acl_tcam_vregion_rehash
+       check_err $? "Rehash trace was not hit"
+       tp_check_hits_any mlxsw:mlxsw_sp_acl_tcam_vregion_migrate
+       check_err $? "Migrate trace was not hit"
+       tp_record_all mlxsw:* 3
+       tp_check_hits_any mlxsw:mlxsw_sp_acl_tcam_vregion_rehash
+       check_err $? "Rehash trace was not hit"
+       tp_check_hits_any mlxsw:mlxsw_sp_acl_tcam_vregion_migrate
+       check_fail $? "Migrate trace was hit when no migration should happen"
+
+       $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+               -t ip -q
+
+       tc_check_packets "dev $h2 ingress" 101 1
+       check_fail $? "Matched a wrong filter after rehash"
+
+       tc_check_packets "dev $h2 ingress" 103 1
+       check_fail $? "Matched a wrong filter after rehash"
+
+       tc_check_packets "dev $h2 ingress" 102 2
+       check_err $? "Did not match on correct filter after rehash"
+
+       tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower
+       tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
+       tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
+
+       log_test "delta simple rehash test ($tcflags)"
+}
+
 bloom_simple_test()
 {
        # Bloom filter requires that the eRP table is used. This test
index a0a80e1..e7ffc79 100755 (executable)
@@ -2,7 +2,6 @@
 # SPDX-License-Identifier: GPL-2.0
 
 NUM_NETIFS=6
-source ../../../../net/forwarding/lib.sh
 source ../../../../net/forwarding/tc_common.sh
 source devlink_lib_spectrum.sh
 
diff --git a/tools/testing/selftests/filesystems/binderfs/.gitignore b/tools/testing/selftests/filesystems/binderfs/.gitignore
new file mode 100644 (file)
index 0000000..8a5d9bf
--- /dev/null
@@ -0,0 +1 @@
+binderfs_test
diff --git a/tools/testing/selftests/filesystems/binderfs/Makefile b/tools/testing/selftests/filesystems/binderfs/Makefile
new file mode 100644 (file)
index 0000000..58cb659
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+CFLAGS += -I../../../../../usr/include/
+TEST_GEN_PROGS := binderfs_test
+
+include ../../lib.mk
diff --git a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
new file mode 100644 (file)
index 0000000..8c2ed96
--- /dev/null
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <linux/android/binder.h>
+#include <linux/android/binderfs.h>
+#include "../../kselftest.h"
+
+static ssize_t write_nointr(int fd, const void *buf, size_t count)
+{
+       ssize_t ret;
+again:
+       ret = write(fd, buf, count);
+       if (ret < 0 && errno == EINTR)
+               goto again;
+
+       return ret;
+}
+
+static void write_to_file(const char *filename, const void *buf, size_t count,
+                         int allowed_errno)
+{
+       int fd, saved_errno;
+       ssize_t ret;
+
+       fd = open(filename, O_WRONLY | O_CLOEXEC);
+       if (fd < 0)
+               ksft_exit_fail_msg("%s - Failed to open file %s\n",
+                                  strerror(errno), filename);
+
+       ret = write_nointr(fd, buf, count);
+       if (ret < 0) {
+               if (allowed_errno && (errno == allowed_errno)) {
+                       close(fd);
+                       return;
+               }
+
+               goto on_error;
+       }
+
+       if ((size_t)ret != count)
+               goto on_error;
+
+       close(fd);
+       return;
+
+on_error:
+       saved_errno = errno;
+       close(fd);
+       errno = saved_errno;
+
+       if (ret < 0)
+               ksft_exit_fail_msg("%s - Failed to write to file %s\n",
+                                  strerror(errno), filename);
+
+       ksft_exit_fail_msg("Failed to write to file %s\n", filename);
+}
+
+static void change_to_userns(void)
+{
+       int ret;
+       uid_t uid;
+       gid_t gid;
+       /* {g,u}id_map files only allow a max of 4096 bytes written to them */
+       char idmap[4096];
+
+       uid = getuid();
+       gid = getgid();
+
+       ret = unshare(CLONE_NEWUSER);
+       if (ret < 0)
+               ksft_exit_fail_msg("%s - Failed to unshare user namespace\n",
+                                  strerror(errno));
+
+       write_to_file("/proc/self/setgroups", "deny", strlen("deny"), ENOENT);
+
+       ret = snprintf(idmap, sizeof(idmap), "0 %d 1", uid);
+       if (ret < 0 || (size_t)ret >= sizeof(idmap))
+               ksft_exit_fail_msg("%s - Failed to prepare uid mapping\n",
+                                  strerror(errno));
+
+       write_to_file("/proc/self/uid_map", idmap, strlen(idmap), 0);
+
+       ret = snprintf(idmap, sizeof(idmap), "0 %d 1", gid);
+       if (ret < 0 || (size_t)ret >= sizeof(idmap))
+               ksft_exit_fail_msg("%s - Failed to prepare uid mapping\n",
+                                  strerror(errno));
+
+       write_to_file("/proc/self/gid_map", idmap, strlen(idmap), 0);
+
+       ret = setgid(0);
+       if (ret)
+               ksft_exit_fail_msg("%s - Failed to setgid(0)\n",
+                                  strerror(errno));
+
+       ret = setuid(0);
+       if (ret)
+               ksft_exit_fail_msg("%s - Failed to setgid(0)\n",
+                                  strerror(errno));
+}
+
+static void change_to_mountns(void)
+{
+       int ret;
+
+       ret = unshare(CLONE_NEWNS);
+       if (ret < 0)
+               ksft_exit_fail_msg("%s - Failed to unshare mount namespace\n",
+                                  strerror(errno));
+
+       ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0);
+       if (ret < 0)
+               ksft_exit_fail_msg("%s - Failed to mount / as private\n",
+                                  strerror(errno));
+}
+
+static void rmdir_protect_errno(const char *dir)
+{
+       int saved_errno = errno;
+       (void)rmdir(dir);
+       errno = saved_errno;
+}
+
+static void __do_binderfs_test(void)
+{
+       int fd, ret, saved_errno;
+       size_t len;
+       ssize_t wret;
+       bool keep = false;
+       struct binderfs_device device = { 0 };
+       struct binder_version version = { 0 };
+
+       change_to_mountns();
+
+       ret = mkdir("/dev/binderfs", 0755);
+       if (ret < 0) {
+               if (errno != EEXIST)
+                       ksft_exit_fail_msg(
+                               "%s - Failed to create binderfs mountpoint\n",
+                               strerror(errno));
+
+               keep = true;
+       }
+
+       ret = mount(NULL, "/dev/binderfs", "binder", 0, 0);
+       if (ret < 0) {
+               if (errno != ENODEV)
+                       ksft_exit_fail_msg("%s - Failed to mount binderfs\n",
+                                          strerror(errno));
+
+               keep ? : rmdir_protect_errno("/dev/binderfs");
+               ksft_exit_skip(
+                       "The Android binderfs filesystem is not available\n");
+       }
+
+       /* binderfs mount test passed */
+       ksft_inc_pass_cnt();
+
+       memcpy(device.name, "my-binder", strlen("my-binder"));
+
+       fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
+       if (fd < 0)
+               ksft_exit_fail_msg(
+                       "%s - Failed to open binder-control device\n",
+                       strerror(errno));
+
+       ret = ioctl(fd, BINDER_CTL_ADD, &device);
+       saved_errno = errno;
+       close(fd);
+       errno = saved_errno;
+       if (ret < 0) {
+               keep ? : rmdir_protect_errno("/dev/binderfs");
+               ksft_exit_fail_msg(
+                       "%s - Failed to allocate new binder device\n",
+                       strerror(errno));
+       }
+
+       ksft_print_msg(
+               "Allocated new binder device with major %d, minor %d, and name %s\n",
+               device.major, device.minor, device.name);
+
+       /* binder device allocation test passed */
+       ksft_inc_pass_cnt();
+
+       fd = open("/dev/binderfs/my-binder", O_CLOEXEC | O_RDONLY);
+       if (fd < 0) {
+               keep ? : rmdir_protect_errno("/dev/binderfs");
+               ksft_exit_fail_msg("%s - Failed to open my-binder device\n",
+                                  strerror(errno));
+       }
+
+       ret = ioctl(fd, BINDER_VERSION, &version);
+       saved_errno = errno;
+       close(fd);
+       errno = saved_errno;
+       if (ret < 0) {
+               keep ? : rmdir_protect_errno("/dev/binderfs");
+               ksft_exit_fail_msg(
+                       "%s - Failed to open perform BINDER_VERSION request\n",
+                       strerror(errno));
+       }
+
+       ksft_print_msg("Detected binder version: %d\n",
+                      version.protocol_version);
+
+       /* binder transaction with binderfs binder device passed */
+       ksft_inc_pass_cnt();
+
+       ret = unlink("/dev/binderfs/my-binder");
+       if (ret < 0) {
+               keep ? : rmdir_protect_errno("/dev/binderfs");
+               ksft_exit_fail_msg("%s - Failed to delete binder device\n",
+                                  strerror(errno));
+       }
+
+       /* binder device removal passed */
+       ksft_inc_pass_cnt();
+
+       ret = unlink("/dev/binderfs/binder-control");
+       if (!ret) {
+               keep ? : rmdir_protect_errno("/dev/binderfs");
+               ksft_exit_fail_msg("Managed to delete binder-control device\n");
+       } else if (errno != EPERM) {
+               keep ? : rmdir_protect_errno("/dev/binderfs");
+               ksft_exit_fail_msg(
+                       "%s - Failed to delete binder-control device but exited with unexpected error code\n",
+                       strerror(errno));
+       }
+
+       /* binder-control device removal failed as expected */
+       ksft_inc_xfail_cnt();
+
+on_error:
+       ret = umount2("/dev/binderfs", MNT_DETACH);
+       keep ?: rmdir_protect_errno("/dev/binderfs");
+       if (ret < 0)
+               ksft_exit_fail_msg("%s - Failed to unmount binderfs\n",
+                                  strerror(errno));
+
+       /* binderfs unmount test passed */
+       ksft_inc_pass_cnt();
+}
+
+static void binderfs_test_privileged()
+{
+       if (geteuid() != 0)
+               ksft_print_msg(
+                       "Tests are not run as root. Skipping privileged tests\n");
+       else
+               __do_binderfs_test();
+}
+
+static void binderfs_test_unprivileged()
+{
+       change_to_userns();
+       __do_binderfs_test();
+}
+
+int main(int argc, char *argv[])
+{
+       binderfs_test_privileged();
+       binderfs_test_unprivileged();
+       ksft_exit_pass();
+}
diff --git a/tools/testing/selftests/filesystems/binderfs/config b/tools/testing/selftests/filesystems/binderfs/config
new file mode 100644 (file)
index 0000000..02dd6cc
--- /dev/null
@@ -0,0 +1,3 @@
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDERFS=y
+CONFIG_ANDROID_BINDER_IPC=y
index f4ba8eb..ad06489 100644 (file)
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 TEST_PROGS := ir_loopback.sh
 TEST_GEN_PROGS_EXTENDED := ir_loopback
+APIDIR := ../../../include/uapi
+CFLAGS += -Wall -O2 -I$(APIDIR)
 
 include ../lib.mk
index f8f3e90..1e6d14d 100644 (file)
@@ -21,6 +21,6 @@ TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls
 KSFT_KHDR_INSTALL := 1
 include ../lib.mk
 
-$(OUTPUT)/reuseport_bpf_numa: LDFLAGS += -lnuma
+$(OUTPUT)/reuseport_bpf_numa: LDLIBS += -lnuma
 $(OUTPUT)/tcp_mmap: LDFLAGS += -lpthread
 $(OUTPUT)/tcp_inq: LDFLAGS += -lpthread
index 5cd2aed..da96eff 100644 (file)
@@ -10,3 +10,5 @@ CONFIG_NET_CLS_FLOWER=m
 CONFIG_NET_SCH_INGRESS=m
 CONFIG_NET_ACT_GACT=m
 CONFIG_VETH=m
+CONFIG_NAMESPACES=y
+CONFIG_NET_NS=y
index e819d04..e2adb53 100644 (file)
@@ -33,3 +33,6 @@ PAUSE_ON_CLEANUP=no
 NETIF_TYPE=veth
 # Whether to create virtual interfaces (veth) or not
 NETIF_CREATE=yes
+# Timeout (in seconds) before ping exits regardless of how many packets have
+# been sent or received
+PING_TIMEOUT=5
index c1f16bb..9385dc9 100644 (file)
@@ -17,6 +17,7 @@ NETIF_TYPE=${NETIF_TYPE:=veth}
 NETIF_CREATE=${NETIF_CREATE:=yes}
 MCD=${MCD:=smcrouted}
 MC_CLI=${MC_CLI:=smcroutectl}
+PING_TIMEOUT=${PING_TIMEOUT:=5}
 
 relative_path="${BASH_SOURCE%/*}"
 if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then
@@ -820,7 +821,8 @@ ping_do()
        local vrf_name
 
        vrf_name=$(master_name_get $if_name)
-       ip vrf exec $vrf_name $PING $args $dip -c 10 -i 0.1 -w 2 &> /dev/null
+       ip vrf exec $vrf_name \
+               $PING $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
 }
 
 ping_test()
@@ -840,7 +842,8 @@ ping6_do()
        local vrf_name
 
        vrf_name=$(master_name_get $if_name)
-       ip vrf exec $vrf_name $PING6 $args $dip -c 10 -i 0.1 -w 2 &> /dev/null
+       ip vrf exec $vrf_name \
+               $PING6 $args $dip -c 10 -i 0.1 -w $PING_TIMEOUT &> /dev/null
 }
 
 ping6_test()
index 61844ca..28d568c 100755 (executable)
@@ -190,6 +190,8 @@ setup_prepare()
        h4_create
        switch_create
 
+       forwarding_enable
+
        trap_install $h3 ingress
        trap_install $h4 ingress
 }
@@ -201,6 +203,8 @@ cleanup()
        trap_uninstall $h4 ingress
        trap_uninstall $h3 ingress
 
+       forwarding_restore
+
        switch_destroy
        h4_destroy
        h3_destroy
@@ -220,11 +224,15 @@ test_lag_slave()
 
        RET=0
 
+       tc filter add dev $swp1 ingress pref 999 \
+               proto 802.1q flower vlan_ethtype arp $tcflags \
+               action pass
        mirror_install $swp1 ingress gt4 \
-                      "proto 802.1q flower vlan_id 333 $tcflags"
+               "proto 802.1q flower vlan_id 333 $tcflags"
 
        # Test connectivity through $up_dev when $down_dev is set down.
        ip link set dev $down_dev down
+       ip neigh flush dev br1
        setup_wait_dev $up_dev
        setup_wait_dev $host_dev
        $ARPING -I br1 192.0.2.130 -qfc 1
@@ -240,6 +248,7 @@ test_lag_slave()
        ip link set dev $up_dev up
        ip link set dev $down_dev up
        mirror_uninstall $swp1 ingress
+       tc filter del dev $swp1 ingress pref 999
 
        log_test "$what ($tcflags)"
 }
index 135902a..472bd02 100755 (executable)
@@ -79,6 +79,7 @@ test_span_gre_ttl()
        mirror_test v$h1 192.0.2.1 192.0.2.2 $h3 77 0
 
        ip link set dev $tundev type $type ttl 50
+       sleep 2
        mirror_test v$h1 192.0.2.1 192.0.2.2 $h3 77 10
 
        ip link set dev $tundev type $type ttl 100
index 12914f4..09389f3 100755 (executable)
@@ -81,6 +81,8 @@ full_test_span_gre_dir_acl()
        local match_dip=$1; shift
        local what=$1; shift
 
+       RET=0
+
        mirror_install $swp1 $direction $tundev \
                       "protocol ip flower $tcflags dst_ip $match_dip"
        fail_test_span_gre_dir $tundev $direction
@@ -108,8 +110,6 @@ test_ip6gretap()
 
 test_all()
 {
-       RET=0
-
        slow_path_trap_install $swp1 ingress
        slow_path_trap_install $swp1 egress
 
index 204b25f..c02291e 100755 (executable)
@@ -1,11 +1,44 @@
 #!/bin/bash
 # SPDX-License-Identifier: GPL-2.0
 
-# This test uses standard topology for testing gretap. See
-# mirror_gre_topo_lib.sh for more details.
-#
 # Test for "tc action mirred egress mirror" when the underlay route points at a
 # vlan device on top of a bridge device with vlan filtering (802.1q).
+#
+#   +---------------------+                             +---------------------+
+#   | H1                  |                             |                  H2 |
+#   |     + $h1           |                             |           $h2 +     |
+#   |     | 192.0.2.1/28  |                             |  192.0.2.2/28 |     |
+#   +-----|---------------+                             +---------------|-----+
+#         |                                                             |
+#   +-----|-------------------------------------------------------------|-----+
+#   | SW  o--> mirred egress mirror dev {gt4,gt6}                       |     |
+#   |     |                                                             |     |
+#   | +---|-------------------------------------------------------------|---+ |
+#   | |   + $swp1                    br1                          $swp2 +   | |
+#   | |                                                                     | |
+#   | |   + $swp3                                                           | |
+#   | +---|-----------------------------------------------------------------+ |
+#   |     |                        |                                          |
+#   |     |                        + br1.555                                  |
+#   |     |                          192.0.2.130/28                           |
+#   |     |                          2001:db8:2::2/64                         |
+#   |     |                                                                   |
+#   |     |                     + gt6 (ip6gretap)      + gt4 (gretap)         |
+#   |     |                     : loc=2001:db8:2::1    : loc=192.0.2.129      |
+#   |     |                     : rem=2001:db8:2::2    : rem=192.0.2.130      |
+#   |     |                     : ttl=100              : ttl=100              |
+#   |     |                     : tos=inherit          : tos=inherit          |
+#   |     |                     :                      :                      |
+#   +-----|---------------------:----------------------:----------------------+
+#         |                     :                      :
+#   +-----|---------------------:----------------------:----------------------+
+#   | H3  + $h3                 + h3-gt6 (ip6gretap)   + h3-gt4 (gretap)      |
+#   |     |                       loc=2001:db8:2::2      loc=192.0.2.130      |
+#   |     + $h3.555               rem=2001:db8:2::1      rem=192.0.2.129      |
+#   |       192.0.2.130/28        ttl=100                ttl=100              |
+#   |       2001:db8:2::2/64      tos=inherit            tos=inherit          |
+#   |                                                                         |
+#   +-------------------------------------------------------------------------+
 
 ALL_TESTS="
        test_gretap
@@ -30,6 +63,15 @@ source mirror_gre_topo_lib.sh
 
 require_command $ARPING
 
+h3_addr_add_del()
+{
+       local add_del=$1; shift
+       local dev=$1; shift
+
+       ip addr $add_del dev $dev 192.0.2.130/28
+       ip addr $add_del dev $dev 2001:db8:2::2/64
+}
+
 setup_prepare()
 {
        h1=${NETIFS[p1]}
@@ -55,7 +97,8 @@ setup_prepare()
        ip route rep 192.0.2.130/32 dev br1.555
        ip -6 route rep 2001:db8:2::2/128 dev br1.555
 
-       vlan_create $h3 555 v$h3 192.0.2.130/28 2001:db8:2::2/64
+       vlan_create $h3 555 v$h3
+       h3_addr_add_del add $h3.555
 
        ip link set dev $swp3 master br1
        bridge vlan add dev $swp3 vid 555
@@ -68,6 +111,8 @@ cleanup()
 
        ip link set dev $swp2 nomaster
        ip link set dev $swp3 nomaster
+
+       h3_addr_add_del del $h3.555
        vlan_destroy $h3 555
        vlan_destroy br1 555
 
@@ -182,13 +227,19 @@ test_span_gre_untagged_egress()
        quick_test_span_gre_dir $tundev ingress
        quick_test_span_vlan_dir $h3 555 ingress
 
+       h3_addr_add_del del $h3.555
        bridge vlan add dev $swp3 vid 555 pvid untagged
-       sleep 1
+       h3_addr_add_del add $h3
+       sleep 5
+
        quick_test_span_gre_dir $tundev ingress
        fail_test_span_vlan_dir $h3 555 ingress
 
+       h3_addr_add_del del $h3
        bridge vlan add dev $swp3 vid 555
-       sleep 1
+       h3_addr_add_del add $h3.555
+       sleep 5
+
        quick_test_span_gre_dir $tundev ingress
        quick_test_span_vlan_dir $h3 555 ingress
 
@@ -218,12 +269,25 @@ test_span_gre_fdb_roaming()
        mirror_install $swp1 ingress $tundev "matchall $tcflags"
        quick_test_span_gre_dir $tundev ingress
 
-       bridge fdb del dev $swp3 $h3mac vlan 555 master
-       bridge fdb add dev $swp2 $h3mac vlan 555 master
-       sleep 1
-       fail_test_span_gre_dir $tundev ingress
-
-       bridge fdb del dev $swp2 $h3mac vlan 555 master
+       while ((RET == 0)); do
+               bridge fdb del dev $swp3 $h3mac vlan 555 master 2>/dev/null
+               bridge fdb add dev $swp2 $h3mac vlan 555 master
+               sleep 1
+               fail_test_span_gre_dir $tundev ingress
+
+               if ! bridge fdb sh dev $swp2 vlan 555 master \
+                   | grep -q $h3mac; then
+                       printf "TEST: %-60s  [RETRY]\n" \
+                               "$what: MAC roaming ($tcflags)"
+                       # ARP or ND probably reprimed the FDB while the test
+                       # was running. We would get a spurious failure.
+                       RET=0
+                       continue
+               fi
+               break
+       done
+
+       bridge fdb del dev $swp2 $h3mac vlan 555 master 2>/dev/null
        # Re-prime FDB
        $ARPING -I br1.555 192.0.2.130 -fqc 1
        sleep 1
index 07991e1..0079759 100644 (file)
@@ -29,9 +29,12 @@ mirror_test()
        local pref=$1; shift
        local expect=$1; shift
 
+       local ping_timeout=$((PING_TIMEOUT * 5))
        local t0=$(tc_rule_stats_get $dev $pref)
        ip vrf exec $vrf_name \
-          ${PING} ${sip:+-I $sip} $dip -c 10 -i 0.1 -w 2 &> /dev/null
+          ${PING} ${sip:+-I $sip} $dip -c 10 -i 0.5 -w $ping_timeout \
+                  &> /dev/null
+       sleep 0.5
        local t1=$(tc_rule_stats_get $dev $pref)
        local delta=$((t1 - t0))
        # Tolerate a couple stray extra packets.
index 7bd2ebb..9a678ec 100755 (executable)
@@ -170,7 +170,8 @@ ping_test_from()
 
        log_info "ping $dip, expected reply from $from"
        ip vrf exec $(master_name_get $oif) \
-       $PING -I $oif $dip -c 10 -i 0.1 -w 2 -b 2>&1 | grep $from &> /dev/null
+               $PING -I $oif $dip -c 10 -i 0.1 -w $PING_TIMEOUT -b 2>&1 \
+               | grep $from &> /dev/null
        check_err_fail $fail $?
 }
 
index ff68ed1..4ac50cc 100644 (file)
@@ -42,7 +42,7 @@ FIXTURE_SETUP(tls)
        len = sizeof(addr);
 
        memset(&tls12, 0, sizeof(tls12));
-       tls12.info.version = TLS_1_2_VERSION;
+       tls12.info.version = TLS_1_3_VERSION;
        tls12.info.cipher_type = TLS_CIPHER_AES_GCM_128;
 
        addr.sin_family = AF_INET;
@@ -763,4 +763,140 @@ TEST_F(tls, control_msg)
        EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
 }
 
+TEST(keysizes) {
+       struct tls12_crypto_info_aes_gcm_256 tls12;
+       struct sockaddr_in addr;
+       int sfd, ret, fd, cfd;
+       socklen_t len;
+       bool notls;
+
+       notls = false;
+       len = sizeof(addr);
+
+       memset(&tls12, 0, sizeof(tls12));
+       tls12.info.version = TLS_1_2_VERSION;
+       tls12.info.cipher_type = TLS_CIPHER_AES_GCM_256;
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = htonl(INADDR_ANY);
+       addr.sin_port = 0;
+
+       fd = socket(AF_INET, SOCK_STREAM, 0);
+       sfd = socket(AF_INET, SOCK_STREAM, 0);
+
+       ret = bind(sfd, &addr, sizeof(addr));
+       ASSERT_EQ(ret, 0);
+       ret = listen(sfd, 10);
+       ASSERT_EQ(ret, 0);
+
+       ret = getsockname(sfd, &addr, &len);
+       ASSERT_EQ(ret, 0);
+
+       ret = connect(fd, &addr, sizeof(addr));
+       ASSERT_EQ(ret, 0);
+
+       ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+       if (ret != 0) {
+               notls = true;
+               printf("Failure setting TCP_ULP, testing without tls\n");
+       }
+
+       if (!notls) {
+               ret = setsockopt(fd, SOL_TLS, TLS_TX, &tls12,
+                                sizeof(tls12));
+               EXPECT_EQ(ret, 0);
+       }
+
+       cfd = accept(sfd, &addr, &len);
+       ASSERT_GE(cfd, 0);
+
+       if (!notls) {
+               ret = setsockopt(cfd, IPPROTO_TCP, TCP_ULP, "tls",
+                                sizeof("tls"));
+               EXPECT_EQ(ret, 0);
+
+               ret = setsockopt(cfd, SOL_TLS, TLS_RX, &tls12,
+                                sizeof(tls12));
+               EXPECT_EQ(ret, 0);
+       }
+
+       close(sfd);
+       close(fd);
+       close(cfd);
+}
+
+TEST(tls12) {
+       int fd, cfd;
+       bool notls;
+
+       struct tls12_crypto_info_aes_gcm_128 tls12;
+       struct sockaddr_in addr;
+       socklen_t len;
+       int sfd, ret;
+
+       notls = false;
+       len = sizeof(addr);
+
+       memset(&tls12, 0, sizeof(tls12));
+       tls12.info.version = TLS_1_2_VERSION;
+       tls12.info.cipher_type = TLS_CIPHER_AES_GCM_128;
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = htonl(INADDR_ANY);
+       addr.sin_port = 0;
+
+       fd = socket(AF_INET, SOCK_STREAM, 0);
+       sfd = socket(AF_INET, SOCK_STREAM, 0);
+
+       ret = bind(sfd, &addr, sizeof(addr));
+       ASSERT_EQ(ret, 0);
+       ret = listen(sfd, 10);
+       ASSERT_EQ(ret, 0);
+
+       ret = getsockname(sfd, &addr, &len);
+       ASSERT_EQ(ret, 0);
+
+       ret = connect(fd, &addr, sizeof(addr));
+       ASSERT_EQ(ret, 0);
+
+       ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+       if (ret != 0) {
+               notls = true;
+               printf("Failure setting TCP_ULP, testing without tls\n");
+       }
+
+       if (!notls) {
+               ret = setsockopt(fd, SOL_TLS, TLS_TX, &tls12,
+                                sizeof(tls12));
+               ASSERT_EQ(ret, 0);
+       }
+
+       cfd = accept(sfd, &addr, &len);
+       ASSERT_GE(cfd, 0);
+
+       if (!notls) {
+               ret = setsockopt(cfd, IPPROTO_TCP, TCP_ULP, "tls",
+                                sizeof("tls"));
+               ASSERT_EQ(ret, 0);
+
+               ret = setsockopt(cfd, SOL_TLS, TLS_RX, &tls12,
+                                sizeof(tls12));
+               ASSERT_EQ(ret, 0);
+       }
+
+       close(sfd);
+
+       char const *test_str = "test_read";
+       int send_len = 10;
+       char buf[10];
+
+       send_len = strlen(test_str) + 1;
+       EXPECT_EQ(send(fd, test_str, send_len, 0), send_len);
+       EXPECT_NE(recv(cfd, buf, send_len, 0), -1);
+       EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
+
+       close(fd);
+       close(cfd);
+}
+
 TEST_HARNESS_MAIN
index 8db35b9..71d7fdc 100755 (executable)
@@ -28,6 +28,19 @@ KEY_AES=0x0123456789abcdef0123456789012345
 SPI1=0x1
 SPI2=0x2
 
+do_esp_policy() {
+    local ns=$1
+    local me=$2
+    local remote=$3
+    local lnet=$4
+    local rnet=$5
+
+    # to encrypt packets as they go out (includes forwarded packets that need encapsulation)
+    ip -net $ns xfrm policy add src $lnet dst $rnet dir out tmpl src $me dst $remote proto esp mode tunnel priority 100 action allow
+    # to fwd decrypted packets after esp processing:
+    ip -net $ns xfrm policy add src $rnet dst $lnet dir fwd tmpl src $remote dst $me proto esp mode tunnel priority 100 action allow
+}
+
 do_esp() {
     local ns=$1
     local me=$2
@@ -40,10 +53,59 @@ do_esp() {
     ip -net $ns xfrm state add src $remote dst $me proto esp spi $spi_in  enc aes $KEY_AES  auth sha1 $KEY_SHA  mode tunnel sel src $rnet dst $lnet
     ip -net $ns xfrm state add src $me  dst $remote proto esp spi $spi_out enc aes $KEY_AES auth sha1 $KEY_SHA mode tunnel sel src $lnet dst $rnet
 
-    # to encrypt packets as they go out (includes forwarded packets that need encapsulation)
-    ip -net $ns xfrm policy add src $lnet dst $rnet dir out tmpl src $me dst $remote proto esp mode tunnel priority 100 action allow
-    # to fwd decrypted packets after esp processing:
-    ip -net $ns xfrm policy add src $rnet dst $lnet dir fwd tmpl src $remote dst $me proto esp mode tunnel priority 100 action allow
+    do_esp_policy $ns $me $remote $lnet $rnet
+}
+
+# add policies with different netmasks, to make sure kernel carries
+# the policies contained within new netmask over when search tree is
+# re-built.
+# peer netns that are supposed to be encapsulated via esp have addresses
+# in the 10.0.1.0/24 and 10.0.2.0/24 subnets, respectively.
+#
+# Adding a policy for '10.0.1.0/23' will make it necessary to
+# alter the prefix of 10.0.1.0 subnet.
+# In case new prefix overlaps with existing node, the node and all
+# policies it carries need to be merged with the existing one(s).
+#
+# Do that here.
+do_overlap()
+{
+    local ns=$1
+
+    # adds new nodes to tree (neither network exists yet in policy database).
+    ip -net $ns xfrm policy add src 10.1.0.0/24 dst 10.0.0.0/24 dir fwd priority 200 action block
+
+    # adds a new node in the 10.0.0.0/24 tree (dst node exists).
+    ip -net $ns xfrm policy add src 10.2.0.0/24 dst 10.0.0.0/24 dir fwd priority 200 action block
+
+    # adds a 10.2.0.0/23 node, but for different dst.
+    ip -net $ns xfrm policy add src 10.2.0.0/23 dst 10.0.1.0/24 dir fwd priority 200 action block
+
+    # dst now overlaps with the 10.0.1.0/24 ESP policy in fwd.
+    # kernel must 'promote' existing one (10.0.0.0/24) to 10.0.0.0/23.
+    # But 10.0.0.0/23 also includes existing 10.0.1.0/24, so that node
+    # also has to be merged too, including source-sorted subtrees.
+    # old:
+    # 10.0.0.0/24 (node 1 in dst tree of the bin)
+    #    10.1.0.0/24 (node in src tree of dst node 1)
+    #    10.2.0.0/24 (node in src tree of dst node 1)
+    # 10.0.1.0/24 (node 2 in dst tree of the bin)
+    #    10.0.2.0/24 (node in src tree of dst node 2)
+    #    10.2.0.0/24 (node in src tree of dst node 2)
+    #
+    # The next 'policy add' adds dst '10.0.0.0/23', which means
+    # that dst node 1 and dst node 2 have to be merged including
+    # the sub-tree.  As no duplicates are allowed, policies in
+    # the two '10.0.2.0/24' are also merged.
+    #
+    # after the 'add', internal search tree should look like this:
+    # 10.0.0.0/23 (node in dst tree of bin)
+    #     10.0.2.0/24 (node in src tree of dst node)
+    #     10.1.0.0/24 (node in src tree of dst node)
+    #     10.2.0.0/24 (node in src tree of dst node)
+    #
+    # 10.0.0.0/24 and 10.0.1.0/24 nodes have been merged as 10.0.0.0/23.
+    ip -net $ns xfrm policy add src 10.1.0.0/24 dst 10.0.0.0/23 dir fwd priority 200 action block
 }
 
 do_esp_policy_get_check() {
@@ -160,6 +222,41 @@ check_xfrm() {
        return $lret
 }
 
+check_exceptions()
+{
+       logpostfix="$1"
+       local lret=0
+
+       # ping to .254 should be excluded from the tunnel (exception is in place).
+       check_xfrm 0 254
+       if [ $? -ne 0 ]; then
+               echo "FAIL: expected ping to .254 to fail ($logpostfix)"
+               lret=1
+       else
+               echo "PASS: ping to .254 bypassed ipsec tunnel ($logpostfix)"
+       fi
+
+       # ping to .253 should use use ipsec due to direct policy exception.
+       check_xfrm 1 253
+       if [ $? -ne 0 ]; then
+               echo "FAIL: expected ping to .253 to use ipsec tunnel ($logpostfix)"
+               lret=1
+       else
+               echo "PASS: direct policy matches ($logpostfix)"
+       fi
+
+       # ping to .2 should use ipsec.
+       check_xfrm 1 2
+       if [ $? -ne 0 ]; then
+               echo "FAIL: expected ping to .2 to use ipsec tunnel ($logpostfix)"
+               lret=1
+       else
+               echo "PASS: policy matches ($logpostfix)"
+       fi
+
+       return $lret
+}
+
 #check for needed privileges
 if [ "$(id -u)" -ne 0 ];then
        echo "SKIP: Need root privileges"
@@ -270,33 +367,45 @@ do_exception ns4 10.0.3.10 10.0.3.1 10.0.1.253 10.0.1.240/28
 do_exception ns3 dead:3::1 dead:3::10 dead:2::fd  dead:2:f0::/96
 do_exception ns4 dead:3::10 dead:3::1 dead:1::fd  dead:1:f0::/96
 
-# ping to .254 should now be excluded from the tunnel
-check_xfrm 0 254
+check_exceptions "exceptions"
 if [ $? -ne 0 ]; then
-       echo "FAIL: expected ping to .254 to fail"
        ret=1
-else
-       echo "PASS: ping to .254 bypassed ipsec tunnel"
 fi
 
-# ping to .253 should use use ipsec due to direct policy exception.
-check_xfrm 1 253
-if [ $? -ne 0 ]; then
-       echo "FAIL: expected ping to .253 to use ipsec tunnel"
-       ret=1
-else
-       echo "PASS: direct policy matches"
-fi
+# insert block policies with adjacent/overlapping netmasks
+do_overlap ns3
 
-# ping to .2 should use ipsec.
-check_xfrm 1 2
+check_exceptions "exceptions and block policies"
 if [ $? -ne 0 ]; then
-       echo "FAIL: expected ping to .2 to use ipsec tunnel"
        ret=1
-else
-       echo "PASS: policy matches"
 fi
 
+for n in ns3 ns4;do
+       ip -net $n xfrm policy set hthresh4 28 24 hthresh6 126 125
+       sleep $((RANDOM%5))
+done
+
+check_exceptions "exceptions and block policies after hresh changes"
+
+# full flush of policy db, check everything gets freed incl. internal meta data
+ip -net ns3 xfrm policy flush
+
+do_esp_policy ns3 10.0.3.1 10.0.3.10 10.0.1.0/24 10.0.2.0/24
+do_exception ns3 10.0.3.1 10.0.3.10 10.0.2.253 10.0.2.240/28
+
+# move inexact policies to hash table
+ip -net ns3 xfrm policy set hthresh4 16 16
+
+sleep $((RANDOM%5))
+check_exceptions "exceptions and block policies after hthresh change in ns3"
+
+# restore original hthresh settings -- move policies back to tables
+for n in ns3 ns4;do
+       ip -net $n xfrm policy set hthresh4 32 32 hthresh6 128 128
+       sleep $((RANDOM%5))
+done
+check_exceptions "exceptions and block policies after hresh change to normal"
+
 for i in 1 2 3 4;do ip netns del ns$i;done
 
 exit $ret
index 47ed6ce..c9ff2b4 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 # Makefile for netfilter selftests
 
-TEST_PROGS := nft_trans_stress.sh
+TEST_PROGS := nft_trans_stress.sh nft_nat.sh
 
 include ../lib.mk
diff --git a/tools/testing/selftests/netfilter/nft_nat.sh b/tools/testing/selftests/netfilter/nft_nat.sh
new file mode 100755 (executable)
index 0000000..8ec7668
--- /dev/null
@@ -0,0 +1,762 @@
+#!/bin/bash
+#
+# This test is for basic NAT functionality: snat, dnat, redirect, masquerade.
+#
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+ret=0
+
+nft --version > /dev/null 2>&1
+if [ $? -ne 0 ];then
+       echo "SKIP: Could not run test without nft tool"
+       exit $ksft_skip
+fi
+
+ip -Version > /dev/null 2>&1
+if [ $? -ne 0 ];then
+       echo "SKIP: Could not run test without ip tool"
+       exit $ksft_skip
+fi
+
+ip netns add ns0
+ip netns add ns1
+ip netns add ns2
+
+ip link add veth0 netns ns0 type veth peer name eth0 netns ns1
+ip link add veth1 netns ns0 type veth peer name eth0 netns ns2
+
+ip -net ns0 link set lo up
+ip -net ns0 link set veth0 up
+ip -net ns0 addr add 10.0.1.1/24 dev veth0
+ip -net ns0 addr add dead:1::1/64 dev veth0
+
+ip -net ns0 link set veth1 up
+ip -net ns0 addr add 10.0.2.1/24 dev veth1
+ip -net ns0 addr add dead:2::1/64 dev veth1
+
+for i in 1 2; do
+  ip -net ns$i link set lo up
+  ip -net ns$i link set eth0 up
+  ip -net ns$i addr add 10.0.$i.99/24 dev eth0
+  ip -net ns$i route add default via 10.0.$i.1
+  ip -net ns$i addr add dead:$i::99/64 dev eth0
+  ip -net ns$i route add default via dead:$i::1
+done
+
+bad_counter()
+{
+       local ns=$1
+       local counter=$2
+       local expect=$3
+
+       echo "ERROR: $counter counter in $ns has unexpected value (expected $expect)" 1>&2
+       ip netns exec $ns nft list counter inet filter $counter 1>&2
+}
+
+check_counters()
+{
+       ns=$1
+       local lret=0
+
+       cnt=$(ip netns exec $ns nft list counter inet filter ns0in | grep -q "packets 1 bytes 84")
+       if [ $? -ne 0 ]; then
+               bad_counter $ns ns0in "packets 1 bytes 84"
+               lret=1
+       fi
+       cnt=$(ip netns exec $ns nft list counter inet filter ns0out | grep -q "packets 1 bytes 84")
+       if [ $? -ne 0 ]; then
+               bad_counter $ns ns0out "packets 1 bytes 84"
+               lret=1
+       fi
+
+       expect="packets 1 bytes 104"
+       cnt=$(ip netns exec $ns nft list counter inet filter ns0in6 | grep -q "$expect")
+       if [ $? -ne 0 ]; then
+               bad_counter $ns ns0in6 "$expect"
+               lret=1
+       fi
+       cnt=$(ip netns exec $ns nft list counter inet filter ns0out6 | grep -q "$expect")
+       if [ $? -ne 0 ]; then
+               bad_counter $ns ns0out6 "$expect"
+               lret=1
+       fi
+
+       return $lret
+}
+
+check_ns0_counters()
+{
+       local ns=$1
+       local lret=0
+
+       cnt=$(ip netns exec ns0 nft list counter inet filter ns0in | grep -q "packets 0 bytes 0")
+       if [ $? -ne 0 ]; then
+               bad_counter ns0 ns0in "packets 0 bytes 0"
+               lret=1
+       fi
+
+       cnt=$(ip netns exec ns0 nft list counter inet filter ns0in6 | grep -q "packets 0 bytes 0")
+       if [ $? -ne 0 ]; then
+               bad_counter ns0 ns0in6 "packets 0 bytes 0"
+               lret=1
+       fi
+
+       cnt=$(ip netns exec ns0 nft list counter inet filter ns0out | grep -q "packets 0 bytes 0")
+       if [ $? -ne 0 ]; then
+               bad_counter ns0 ns0out "packets 0 bytes 0"
+               lret=1
+       fi
+       cnt=$(ip netns exec ns0 nft list counter inet filter ns0out6 | grep -q "packets 0 bytes 0")
+       if [ $? -ne 0 ]; then
+               bad_counter ns0 ns0out6 "packets 0 bytes 0"
+               lret=1
+       fi
+
+       for dir in "in" "out" ; do
+               expect="packets 1 bytes 84"
+               cnt=$(ip netns exec ns0 nft list counter inet filter ${ns}${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns0 $ns$dir "$expect"
+                       lret=1
+               fi
+
+               expect="packets 1 bytes 104"
+               cnt=$(ip netns exec ns0 nft list counter inet filter ${ns}${dir}6 | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns0 $ns$dir6 "$expect"
+                       lret=1
+               fi
+       done
+
+       return $lret
+}
+
+reset_counters()
+{
+       for i in 0 1 2;do
+               ip netns exec ns$i nft reset counters inet > /dev/null
+       done
+}
+
+test_local_dnat6()
+{
+       local lret=0
+ip netns exec ns0 nft -f - <<EOF
+table ip6 nat {
+       chain output {
+               type nat hook output priority 0; policy accept;
+               ip6 daddr dead:1::99 dnat to dead:2::99
+       }
+}
+EOF
+       if [ $? -ne 0 ]; then
+               echo "SKIP: Could not add add ip6 dnat hook"
+               return $ksft_skip
+       fi
+
+       # ping netns1, expect rewrite to netns2
+       ip netns exec ns0 ping -q -c 1 dead:1::99 > /dev/null
+       if [ $? -ne 0 ]; then
+               lret=1
+               echo "ERROR: ping6 failed"
+               return $lret
+       fi
+
+       expect="packets 0 bytes 0"
+       for dir in "in6" "out6" ; do
+               cnt=$(ip netns exec ns0 nft list counter inet filter ns1${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns0 ns1$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       expect="packets 1 bytes 104"
+       for dir in "in6" "out6" ; do
+               cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns0 ns2$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       # expect 0 count in ns1
+       expect="packets 0 bytes 0"
+       for dir in "in6" "out6" ; do
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns0$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       # expect 1 packet in ns2
+       expect="packets 1 bytes 104"
+       for dir in "in6" "out6" ; do
+               cnt=$(ip netns exec ns2 nft list counter inet filter ns0${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns2 ns0$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       test $lret -eq 0 && echo "PASS: ipv6 ping to ns1 was NATted to ns2"
+       ip netns exec ns0 nft flush chain ip6 nat output
+
+       return $lret
+}
+
+test_local_dnat()
+{
+       local lret=0
+ip netns exec ns0 nft -f - <<EOF
+table ip nat {
+       chain output {
+               type nat hook output priority 0; policy accept;
+               ip daddr 10.0.1.99 dnat to 10.0.2.99
+       }
+}
+EOF
+       # ping netns1, expect rewrite to netns2
+       ip netns exec ns0 ping -q -c 1 10.0.1.99 > /dev/null
+       if [ $? -ne 0 ]; then
+               lret=1
+               echo "ERROR: ping failed"
+               return $lret
+       fi
+
+       expect="packets 0 bytes 0"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns0 nft list counter inet filter ns1${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns0 ns1$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       expect="packets 1 bytes 84"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns0 ns2$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       # expect 0 count in ns1
+       expect="packets 0 bytes 0"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns0$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       # expect 1 packet in ns2
+       expect="packets 1 bytes 84"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns2 nft list counter inet filter ns0${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns2 ns0$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       test $lret -eq 0 && echo "PASS: ping to ns1 was NATted to ns2"
+
+       ip netns exec ns0 nft flush chain ip nat output
+
+       reset_counters
+       ip netns exec ns0 ping -q -c 1 10.0.1.99 > /dev/null
+       if [ $? -ne 0 ]; then
+               lret=1
+               echo "ERROR: ping failed"
+               return $lret
+       fi
+
+       expect="packets 1 bytes 84"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns0 nft list counter inet filter ns1${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns1$dir "$expect"
+                       lret=1
+               fi
+       done
+       expect="packets 0 bytes 0"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns0 ns2$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       # expect 1 count in ns1
+       expect="packets 1 bytes 84"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns0 ns0$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       # expect 0 packet in ns2
+       expect="packets 0 bytes 0"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns2 nft list counter inet filter ns0${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns2 ns2$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       test $lret -eq 0 && echo "PASS: ping to ns1 OK after nat output chain flush"
+
+       return $lret
+}
+
+
+test_masquerade6()
+{
+       local lret=0
+
+       ip netns exec ns0 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
+
+       ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
+       if [ $? -ne 0 ] ; then
+               echo "ERROR: cannot ping ns1 from ns2 via ipv6"
+               return 1
+               lret=1
+       fi
+
+       expect="packets 1 bytes 104"
+       for dir in "in6" "out6" ; do
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns2$dir "$expect"
+                       lret=1
+               fi
+
+               cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns2 ns1$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       reset_counters
+
+# add masquerading rule
+ip netns exec ns0 nft -f - <<EOF
+table ip6 nat {
+       chain postrouting {
+               type nat hook postrouting priority 0; policy accept;
+               meta oif veth0 masquerade
+       }
+}
+EOF
+       ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
+       if [ $? -ne 0 ] ; then
+               echo "ERROR: cannot ping ns1 from ns2 with active ipv6 masquerading"
+               lret=1
+       fi
+
+       # ns1 should have seen packets from ns0, due to masquerade
+       expect="packets 1 bytes 104"
+       for dir in "in6" "out6" ; do
+
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns0$dir "$expect"
+                       lret=1
+               fi
+
+               cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns2 ns1$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       # ns1 should not have seen packets from ns2, due to masquerade
+       expect="packets 0 bytes 0"
+       for dir in "in6" "out6" ; do
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns0$dir "$expect"
+                       lret=1
+               fi
+
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns2 ns1$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       ip netns exec ns0 nft flush chain ip6 nat postrouting
+       if [ $? -ne 0 ]; then
+               echo "ERROR: Could not flush ip6 nat postrouting" 1>&2
+               lret=1
+       fi
+
+       test $lret -eq 0 && echo "PASS: IPv6 masquerade for ns2"
+
+       return $lret
+}
+
+test_masquerade()
+{
+       local lret=0
+
+       ip netns exec ns0 sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
+       ip netns exec ns0 sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
+
+       ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
+       if [ $? -ne 0 ] ; then
+               echo "ERROR: canot ping ns1 from ns2"
+               lret=1
+       fi
+
+       expect="packets 1 bytes 84"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns2$dir "$expect"
+                       lret=1
+               fi
+
+               cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns2 ns1$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       reset_counters
+
+# add masquerading rule
+ip netns exec ns0 nft -f - <<EOF
+table ip nat {
+       chain postrouting {
+               type nat hook postrouting priority 0; policy accept;
+               meta oif veth0 masquerade
+       }
+}
+EOF
+       ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
+       if [ $? -ne 0 ] ; then
+               echo "ERROR: cannot ping ns1 from ns2 with active ip masquerading"
+               lret=1
+       fi
+
+       # ns1 should have seen packets from ns0, due to masquerade
+       expect="packets 1 bytes 84"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns0$dir "$expect"
+                       lret=1
+               fi
+
+               cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns2 ns1$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       # ns1 should not have seen packets from ns2, due to masquerade
+       expect="packets 0 bytes 0"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns0$dir "$expect"
+                       lret=1
+               fi
+
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns2 ns1$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       ip netns exec ns0 nft flush chain ip nat postrouting
+       if [ $? -ne 0 ]; then
+               echo "ERROR: Could not flush nat postrouting" 1>&2
+               lret=1
+       fi
+
+       test $lret -eq 0 && echo "PASS: IP masquerade for ns2"
+
+       return $lret
+}
+
+test_redirect6()
+{
+       local lret=0
+
+       ip netns exec ns0 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
+
+       ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
+       if [ $? -ne 0 ] ; then
+               echo "ERROR: cannnot ping ns1 from ns2 via ipv6"
+               lret=1
+       fi
+
+       expect="packets 1 bytes 104"
+       for dir in "in6" "out6" ; do
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns2$dir "$expect"
+                       lret=1
+               fi
+
+               cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns2 ns1$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       reset_counters
+
+# add redirect rule
+ip netns exec ns0 nft -f - <<EOF
+table ip6 nat {
+       chain prerouting {
+               type nat hook prerouting priority 0; policy accept;
+               meta iif veth1 meta l4proto icmpv6 ip6 saddr dead:2::99 ip6 daddr dead:1::99 redirect
+       }
+}
+EOF
+       ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
+       if [ $? -ne 0 ] ; then
+               echo "ERROR: cannot ping ns1 from ns2 with active ip6 redirect"
+               lret=1
+       fi
+
+       # ns1 should have seen no packets from ns2, due to redirection
+       expect="packets 0 bytes 0"
+       for dir in "in6" "out6" ; do
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns0$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       # ns0 should have seen packets from ns2, due to masquerade
+       expect="packets 1 bytes 104"
+       for dir in "in6" "out6" ; do
+               cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns0$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       ip netns exec ns0 nft delete table ip6 nat
+       if [ $? -ne 0 ]; then
+               echo "ERROR: Could not delete ip6 nat table" 1>&2
+               lret=1
+       fi
+
+       test $lret -eq 0 && echo "PASS: IPv6 redirection for ns2"
+
+       return $lret
+}
+
+test_redirect()
+{
+       local lret=0
+
+       ip netns exec ns0 sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
+       ip netns exec ns0 sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
+
+       ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
+       if [ $? -ne 0 ] ; then
+               echo "ERROR: cannot ping ns1 from ns2"
+               lret=1
+       fi
+
+       expect="packets 1 bytes 84"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns2$dir "$expect"
+                       lret=1
+               fi
+
+               cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns2 ns1$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       reset_counters
+
+# add redirect rule
+ip netns exec ns0 nft -f - <<EOF
+table ip nat {
+       chain prerouting {
+               type nat hook prerouting priority 0; policy accept;
+               meta iif veth1 ip protocol icmp ip saddr 10.0.2.99 ip daddr 10.0.1.99 redirect
+       }
+}
+EOF
+       ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
+       if [ $? -ne 0 ] ; then
+               echo "ERROR: cannot ping ns1 from ns2 with active ip redirect"
+               lret=1
+       fi
+
+       # ns1 should have seen no packets from ns2, due to redirection
+       expect="packets 0 bytes 0"
+       for dir in "in" "out" ; do
+
+               cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns0$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       # ns0 should have seen packets from ns2, due to masquerade
+       expect="packets 1 bytes 84"
+       for dir in "in" "out" ; do
+               cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect")
+               if [ $? -ne 0 ]; then
+                       bad_counter ns1 ns0$dir "$expect"
+                       lret=1
+               fi
+       done
+
+       ip netns exec ns0 nft delete table ip nat
+       if [ $? -ne 0 ]; then
+               echo "ERROR: Could not delete nat table" 1>&2
+               lret=1
+       fi
+
+       test $lret -eq 0 && echo "PASS: IP redirection for ns2"
+
+       return $lret
+}
+
+
+# ip netns exec ns0 ping -c 1 -q 10.0.$i.99
+for i in 0 1 2; do
+ip netns exec ns$i nft -f - <<EOF
+table inet filter {
+       counter ns0in {}
+       counter ns1in {}
+       counter ns2in {}
+
+       counter ns0out {}
+       counter ns1out {}
+       counter ns2out {}
+
+       counter ns0in6 {}
+       counter ns1in6 {}
+       counter ns2in6 {}
+
+       counter ns0out6 {}
+       counter ns1out6 {}
+       counter ns2out6 {}
+
+       map nsincounter {
+               type ipv4_addr : counter
+               elements = { 10.0.1.1 : "ns0in",
+                            10.0.2.1 : "ns0in",
+                            10.0.1.99 : "ns1in",
+                            10.0.2.99 : "ns2in" }
+       }
+
+       map nsincounter6 {
+               type ipv6_addr : counter
+               elements = { dead:1::1 : "ns0in6",
+                            dead:2::1 : "ns0in6",
+                            dead:1::99 : "ns1in6",
+                            dead:2::99 : "ns2in6" }
+       }
+
+       map nsoutcounter {
+               type ipv4_addr : counter
+               elements = { 10.0.1.1 : "ns0out",
+                            10.0.2.1 : "ns0out",
+                            10.0.1.99: "ns1out",
+                            10.0.2.99: "ns2out" }
+       }
+
+       map nsoutcounter6 {
+               type ipv6_addr : counter
+               elements = { dead:1::1 : "ns0out6",
+                            dead:2::1 : "ns0out6",
+                            dead:1::99 : "ns1out6",
+                            dead:2::99 : "ns2out6" }
+       }
+
+       chain input {
+               type filter hook input priority 0; policy accept;
+               counter name ip saddr map @nsincounter
+               icmpv6 type { "echo-request", "echo-reply" } counter name ip6 saddr map @nsincounter6
+       }
+       chain output {
+               type filter hook output priority 0; policy accept;
+               counter name ip daddr map @nsoutcounter
+               icmpv6 type { "echo-request", "echo-reply" } counter name ip6 daddr map @nsoutcounter6
+       }
+}
+EOF
+done
+
+sleep 3
+# test basic connectivity
+for i in 1 2; do
+  ip netns exec ns0 ping -c 1 -q 10.0.$i.99 > /dev/null
+  if [ $? -ne 0 ];then
+       echo "ERROR: Could not reach other namespace(s)" 1>&2
+       ret=1
+  fi
+
+  ip netns exec ns0 ping -c 1 -q dead:$i::99 > /dev/null
+  if [ $? -ne 0 ];then
+       echo "ERROR: Could not reach other namespace(s) via ipv6" 1>&2
+       ret=1
+  fi
+  check_counters ns$i
+  if [ $? -ne 0 ]; then
+       ret=1
+  fi
+
+  check_ns0_counters ns$i
+  if [ $? -ne 0 ]; then
+       ret=1
+  fi
+  reset_counters
+done
+
+if [ $ret -eq 0 ];then
+       echo "PASS: netns routing/connectivity: ns0 can reach ns1 and ns2"
+fi
+
+reset_counters
+test_local_dnat
+test_local_dnat6
+
+reset_counters
+test_masquerade
+test_masquerade6
+
+reset_counters
+test_redirect
+test_redirect6
+
+for i in 0 1 2; do ip netns del ns$i;done
+
+exit $ret
index 9050eee..1de8bd8 100644 (file)
@@ -9,6 +9,3 @@ all: $(TEST_PROGS)
 top_srcdir = ../../../../..
 KSFT_KHDR_INSTALL := 1
 include ../../lib.mk
-
-clean:
-       rm -fr $(TEST_GEN_FILES)
index dd4162f..6dee9e6 100644 (file)
@@ -5,6 +5,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <sys/time.h>
 #include <sys/socket.h>
index 82121a8..29bac5e 100644 (file)
@@ -10,4 +10,5 @@
 /proc-uptime-002
 /read
 /self
+/setns-dcache
 /thread-self
index 1c12c34..434d033 100644 (file)
@@ -14,6 +14,7 @@ TEST_GEN_PROGS += proc-uptime-001
 TEST_GEN_PROGS += proc-uptime-002
 TEST_GEN_PROGS += read
 TEST_GEN_PROGS += self
+TEST_GEN_PROGS += setns-dcache
 TEST_GEN_PROGS += thread-self
 
 include ../lib.mk
diff --git a/tools/testing/selftests/proc/setns-dcache.c b/tools/testing/selftests/proc/setns-dcache.c
new file mode 100644 (file)
index 0000000..60ab197
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright © 2019 Alexey Dobriyan <adobriyan@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Test that setns(CLONE_NEWNET) points to new /proc/net content even
+ * if old one is in dcache.
+ *
+ * FIXME /proc/net/unix is under CONFIG_UNIX which can be disabled.
+ */
+#undef NDEBUG
+#include <assert.h>
+#include <errno.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+static pid_t pid = -1;
+
+static void f(void)
+{
+       if (pid > 0) {
+               kill(pid, SIGTERM);
+       }
+}
+
+int main(void)
+{
+       int fd[2];
+       char _ = 0;
+       int nsfd;
+
+       atexit(f);
+
+       /* Check for priviledges and syscall availability straight away. */
+       if (unshare(CLONE_NEWNET) == -1) {
+               if (errno == ENOSYS || errno == EPERM) {
+                       return 4;
+               }
+               return 1;
+       }
+       /* Distinguisher between two otherwise empty net namespaces. */
+       if (socket(AF_UNIX, SOCK_STREAM, 0) == -1) {
+               return 1;
+       }
+
+       if (pipe(fd) == -1) {
+               return 1;
+       }
+
+       pid = fork();
+       if (pid == -1) {
+               return 1;
+       }
+
+       if (pid == 0) {
+               if (unshare(CLONE_NEWNET) == -1) {
+                       return 1;
+               }
+
+               if (write(fd[1], &_, 1) != 1) {
+                       return 1;
+               }
+
+               pause();
+
+               return 0;
+       }
+
+       if (read(fd[0], &_, 1) != 1) {
+               return 1;
+       }
+
+       {
+               char buf[64];
+               snprintf(buf, sizeof(buf), "/proc/%u/ns/net", pid);
+               nsfd = open(buf, O_RDONLY);
+               if (nsfd == -1) {
+                       return 1;
+               }
+       }
+
+       /* Reliably pin dentry into dcache. */
+       (void)open("/proc/net/unix", O_RDONLY);
+
+       if (setns(nsfd, CLONE_NEWNET) == -1) {
+               return 1;
+       }
+
+       kill(pid, SIGTERM);
+       pid = 0;
+
+       {
+               char buf[4096];
+               ssize_t rv;
+               int fd;
+
+               fd = open("/proc/net/unix", O_RDONLY);
+               if (fd == -1) {
+                       return 1;
+               }
+
+#define S "Num       RefCount Protocol Flags    Type St Inode Path\n"
+               rv = read(fd, buf, sizeof(buf));
+
+               assert(rv == strlen(S));
+               assert(memcmp(buf, S, strlen(S)) == 0);
+       }
+
+       return 0;
+}
index 496a9a8..7e632b4 100644 (file)
@@ -1608,7 +1608,16 @@ TEST_F(TRACE_poke, getpid_runs_normally)
 #ifdef SYSCALL_NUM_RET_SHARE_REG
 # define EXPECT_SYSCALL_RETURN(val, action)    EXPECT_EQ(-1, action)
 #else
-# define EXPECT_SYSCALL_RETURN(val, action)    EXPECT_EQ(val, action)
+# define EXPECT_SYSCALL_RETURN(val, action)            \
+       do {                                            \
+               errno = 0;                              \
+               if (val < 0) {                          \
+                       EXPECT_EQ(-1, action);          \
+                       EXPECT_EQ(-(val), errno);       \
+               } else {                                \
+                       EXPECT_EQ(val, action);         \
+               }                                       \
+       } while (0)
 #endif
 
 /* Use PTRACE_GETREGS and PTRACE_SETREGS when available. This is useful for
@@ -1647,7 +1656,7 @@ int get_syscall(struct __test_metadata *_metadata, pid_t tracee)
 
 /* Architecture-specific syscall changing routine. */
 void change_syscall(struct __test_metadata *_metadata,
-                   pid_t tracee, int syscall)
+                   pid_t tracee, int syscall, int result)
 {
        int ret;
        ARCH_REGS regs;
@@ -1706,7 +1715,7 @@ void change_syscall(struct __test_metadata *_metadata,
 #ifdef SYSCALL_NUM_RET_SHARE_REG
                TH_LOG("Can't modify syscall return on this architecture");
 #else
-               regs.SYSCALL_RET = EPERM;
+               regs.SYSCALL_RET = result;
 #endif
 
 #ifdef HAVE_GETREGS
@@ -1734,14 +1743,19 @@ void tracer_syscall(struct __test_metadata *_metadata, pid_t tracee,
        case 0x1002:
                /* change getpid to getppid. */
                EXPECT_EQ(__NR_getpid, get_syscall(_metadata, tracee));
-               change_syscall(_metadata, tracee, __NR_getppid);
+               change_syscall(_metadata, tracee, __NR_getppid, 0);
                break;
        case 0x1003:
-               /* skip gettid. */
+               /* skip gettid with valid return code. */
                EXPECT_EQ(__NR_gettid, get_syscall(_metadata, tracee));
-               change_syscall(_metadata, tracee, -1);
+               change_syscall(_metadata, tracee, -1, 45000);
                break;
        case 0x1004:
+               /* skip openat with error. */
+               EXPECT_EQ(__NR_openat, get_syscall(_metadata, tracee));
+               change_syscall(_metadata, tracee, -1, -ESRCH);
+               break;
+       case 0x1005:
                /* do nothing (allow getppid) */
                EXPECT_EQ(__NR_getppid, get_syscall(_metadata, tracee));
                break;
@@ -1774,9 +1788,11 @@ void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee,
        nr = get_syscall(_metadata, tracee);
 
        if (nr == __NR_getpid)
-               change_syscall(_metadata, tracee, __NR_getppid);
+               change_syscall(_metadata, tracee, __NR_getppid, 0);
+       if (nr == __NR_gettid)
+               change_syscall(_metadata, tracee, -1, 45000);
        if (nr == __NR_openat)
-               change_syscall(_metadata, tracee, -1);
+               change_syscall(_metadata, tracee, -1, -ESRCH);
 }
 
 FIXTURE_DATA(TRACE_syscall) {
@@ -1793,8 +1809,10 @@ FIXTURE_SETUP(TRACE_syscall)
                BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1002),
                BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_gettid, 0, 1),
                BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1003),
-               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_openat, 0, 1),
                BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1004),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
+               BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1005),
                BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
        };
 
@@ -1842,15 +1860,26 @@ TEST_F(TRACE_syscall, ptrace_syscall_redirected)
        EXPECT_NE(self->mypid, syscall(__NR_getpid));
 }
 
-TEST_F(TRACE_syscall, ptrace_syscall_dropped)
+TEST_F(TRACE_syscall, ptrace_syscall_errno)
+{
+       /* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
+       teardown_trace_fixture(_metadata, self->tracer);
+       self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
+                                          true);
+
+       /* Tracer should skip the open syscall, resulting in ESRCH. */
+       EXPECT_SYSCALL_RETURN(-ESRCH, syscall(__NR_openat));
+}
+
+TEST_F(TRACE_syscall, ptrace_syscall_faked)
 {
        /* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
        teardown_trace_fixture(_metadata, self->tracer);
        self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
                                           true);
 
-       /* Tracer should skip the open syscall, resulting in EPERM. */
-       EXPECT_SYSCALL_RETURN(EPERM, syscall(__NR_openat));
+       /* Tracer should skip the gettid syscall, resulting fake pid. */
+       EXPECT_SYSCALL_RETURN(45000, syscall(__NR_gettid));
 }
 
 TEST_F(TRACE_syscall, syscall_allowed)
@@ -1883,7 +1912,21 @@ TEST_F(TRACE_syscall, syscall_redirected)
        EXPECT_NE(self->mypid, syscall(__NR_getpid));
 }
 
-TEST_F(TRACE_syscall, syscall_dropped)
+TEST_F(TRACE_syscall, syscall_errno)
+{
+       long ret;
+
+       ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
+       ASSERT_EQ(0, ret);
+
+       /* openat has been skipped and an errno return. */
+       EXPECT_SYSCALL_RETURN(-ESRCH, syscall(__NR_openat));
+}
+
+TEST_F(TRACE_syscall, syscall_faked)
 {
        long ret;
 
@@ -1894,8 +1937,7 @@ TEST_F(TRACE_syscall, syscall_dropped)
        ASSERT_EQ(0, ret);
 
        /* gettid has been skipped and an altered return value stored. */
-       EXPECT_SYSCALL_RETURN(EPERM, syscall(__NR_gettid));
-       EXPECT_NE(self->mytid, syscall(__NR_gettid));
+       EXPECT_SYSCALL_RETURN(45000, syscall(__NR_gettid));
 }
 
 TEST_F(TRACE_syscall, skip_after_RET_TRACE)
index c02683c..7656c7c 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 CFLAGS += -O3 -Wl,-no-as-needed -Wall
-LDFLAGS += -lrt -lpthread -lm
+LDLIBS += -lrt -lpthread -lm
 
 # these are all "safe" tests that don't modify
 # system time or require escalated privileges
index 460b4bd..5d546dc 100644 (file)
@@ -1133,6 +1133,21 @@ void test_pkey_syscalls_bad_args(int *ptr, u16 pkey)
        pkey_assert(err);
 }
 
+void become_child(void)
+{
+       pid_t forkret;
+
+       forkret = fork();
+       pkey_assert(forkret >= 0);
+       dprintf3("[%d] fork() ret: %d\n", getpid(), forkret);
+
+       if (!forkret) {
+               /* in the child */
+               return;
+       }
+       exit(0);
+}
+
 /* Assumes that all pkeys other than 'pkey' are unallocated */
 void test_pkey_alloc_exhaust(int *ptr, u16 pkey)
 {
@@ -1141,7 +1156,7 @@ void test_pkey_alloc_exhaust(int *ptr, u16 pkey)
        int nr_allocated_pkeys = 0;
        int i;
 
-       for (i = 0; i < NR_PKEYS*2; i++) {
+       for (i = 0; i < NR_PKEYS*3; i++) {
                int new_pkey;
                dprintf1("%s() alloc loop: %d\n", __func__, i);
                new_pkey = alloc_pkey();
@@ -1152,21 +1167,27 @@ void test_pkey_alloc_exhaust(int *ptr, u16 pkey)
                if ((new_pkey == -1) && (errno == ENOSPC)) {
                        dprintf2("%s() failed to allocate pkey after %d tries\n",
                                __func__, nr_allocated_pkeys);
-                       break;
+               } else {
+                       /*
+                        * Ensure the number of successes never
+                        * exceeds the number of keys supported
+                        * in the hardware.
+                        */
+                       pkey_assert(nr_allocated_pkeys < NR_PKEYS);
+                       allocated_pkeys[nr_allocated_pkeys++] = new_pkey;
                }
-               pkey_assert(nr_allocated_pkeys < NR_PKEYS);
-               allocated_pkeys[nr_allocated_pkeys++] = new_pkey;
+
+               /*
+                * Make sure that allocation state is properly
+                * preserved across fork().
+                */
+               if (i == NR_PKEYS*2)
+                       become_child();
        }
 
        dprintf3("%s()::%d\n", __func__, __LINE__);
 
        /*
-        * ensure it did not reach the end of the loop without
-        * failure:
-        */
-       pkey_assert(i < NR_PKEYS*2);
-
-       /*
         * There are 16 pkeys supported in hardware.  Three are
         * allocated by the time we get here:
         *   1. The default key (0)
index 5ecea81..5858452 100644 (file)
@@ -3000,8 +3000,10 @@ static int kvm_ioctl_create_device(struct kvm *kvm,
        if (ops->init)
                ops->init(dev);
 
+       kvm_get_kvm(kvm);
        ret = anon_inode_getfd(ops->name, &kvm_device_fops, dev, O_RDWR | O_CLOEXEC);
        if (ret < 0) {
+               kvm_put_kvm(kvm);
                mutex_lock(&kvm->lock);
                list_del(&dev->vm_node);
                mutex_unlock(&kvm->lock);
@@ -3009,7 +3011,6 @@ static int kvm_ioctl_create_device(struct kvm *kvm,
                return ret;
        }
 
-       kvm_get_kvm(kvm);
        cd->fd = ret;
        return 0;
 }